11506 smatch resync
1 /* 2 * Copyright (C) 2016 Oracle. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt 16 */ 17 18 /* 19 * The point here is to store the relationships between two variables. 20 * Ie: y > x. 21 * To do that we create a state with the two variables in alphabetical order: 22 * ->name = "x vs y" and the state would be "<". On the false path the state 23 * would be ">=". 24 * 25 * Part of the trick of it is that if x or y is modified then we need to reset 26 * the state. We need to keep a list of all the states which depend on x and 27 * all the states which depend on y. The link_id code handles this. 28 * 29 */ 30 31 #include "smatch.h" 32 #include "smatch_extra.h" 33 #include "smatch_slist.h" 34 35 static int compare_id; 36 static int link_id; 37 38 static struct smatch_state *alloc_link_state(struct string_list *links) 39 { 40 struct smatch_state *state; 41 static char buf[256]; 42 char *tmp; 43 int i; 44 45 state = __alloc_smatch_state(0); 46 47 i = 0; 48 FOR_EACH_PTR(links, tmp) { 49 if (!i++) { 50 snprintf(buf, sizeof(buf), "%s", tmp); 51 } else { 52 append(buf, ", ", sizeof(buf)); 53 append(buf, tmp, sizeof(buf)); 54 } 55 } END_FOR_EACH_PTR(tmp); 56 57 state->name = alloc_sname(buf); 58 state->data = links; 59 return state; 60 } 61 62 static struct smatch_state *merge_links(struct smatch_state *s1, struct smatch_state *s2) 63 { 64 struct smatch_state *ret; 65 struct string_list *links; 66 67 links = combine_string_lists(s1->data, s2->data); 68 ret = alloc_link_state(links); 69 return ret; 70 } 71 72 static void save_link_var_sym(const char *var, struct symbol *sym, const char *link) 73 { 74 struct smatch_state *old_state, *new_state; 75 struct string_list *links; 76 char *new; 77 78 old_state = get_state(link_id, var, sym); 79 if (old_state) 80 links = clone_str_list(old_state->data); 81 else 82 links = NULL; 83 84 new = alloc_sname(link); 85 insert_string(&links, new); 86 87 new_state = alloc_link_state(links); 88 set_state(link_id, var, sym, new_state); 89 } 90 91 static void add_comparison_var_sym(const char *left_name, 92 struct var_sym_list *left_vsl, 93 int comparison, 94 const char *right_name, struct var_sym_list *right_vsl) 95 { 96 struct smatch_state *state; 97 struct var_sym *vs; 98 char state_name[256]; 99 100 if (strcmp(left_name, right_name) > 0) { 101 const char *tmp_name = left_name; 102 struct var_sym_list *tmp_vsl = left_vsl; 103 104 left_name = right_name; 105 left_vsl = right_vsl; 106 right_name = tmp_name; 107 right_vsl = tmp_vsl; 108 comparison = flip_comparison(comparison); 109 } 110 snprintf(state_name, sizeof(state_name), "%s vs %s", left_name, right_name); 111 state = alloc_compare_state(NULL, left_name, left_vsl, comparison, NULL, right_name, right_vsl); 112 113 set_state(compare_id, state_name, NULL, state); 114 115 FOR_EACH_PTR(left_vsl, vs) { 116 save_link_var_sym(vs->var, vs->sym, state_name); 117 } END_FOR_EACH_PTR(vs); 118 FOR_EACH_PTR(right_vsl, vs) { 119 save_link_var_sym(vs->var, vs->sym, state_name); 120 } END_FOR_EACH_PTR(vs); 121 } 122 123 /* 124 * This is quite a bit more limitted, less ambitious, simpler compared to 125 * smatch_camparison.c. 126 */ 127 void __compare_param_limit_hook(struct expression *left_expr, struct expression *right_expr, 128 const char *state_name, 129 struct smatch_state *true_state, struct smatch_state *false_state) 130 { 131 char *left_name = NULL; 132 char *right_name = NULL; 133 char *tmp_name = NULL; 134 struct symbol *left_sym, *right_sym, *tmp_sym; 135 136 left_name = expr_to_var_sym(left_expr, &left_sym); 137 if (!left_name || !left_sym) 138 goto free; 139 right_name = expr_to_var_sym(right_expr, &right_sym); 140 if (!right_name || !right_sym) 141 goto free; 142 143 if (get_param_num_from_sym(left_sym) < 0 || 144 get_param_num_from_sym(right_sym) < 0) 145 return; 146 147 tmp_name = get_other_name_sym(left_name, left_sym, &tmp_sym); 148 if (tmp_name) { 149 free_string(left_name); 150 left_name = tmp_name; 151 left_sym = tmp_sym; 152 } 153 154 tmp_name = get_other_name_sym(right_name, right_sym, &tmp_sym); 155 if (tmp_name) { 156 free_string(right_name); 157 right_name = tmp_name; 158 right_sym = tmp_sym; 159 } 160 161 if (param_was_set_var_sym(left_name, left_sym)) 162 return; 163 if (param_was_set_var_sym(right_name, right_sym)) 164 return; 165 166 set_true_false_states(compare_id, state_name, NULL, true_state, false_state); 167 save_link_var_sym(left_name, left_sym, state_name); 168 save_link_var_sym(right_name, right_sym, state_name); 169 free: 170 free_string(left_name); 171 free_string(right_name); 172 } 173 174 static void print_return_comparison(int return_id, char *return_ranges, struct expression *expr) 175 { 176 struct sm_state *tmp; 177 struct string_list *links; 178 char *link; 179 struct sm_state *sm; 180 struct compare_data *data; 181 struct var_sym *left, *right; 182 int left_param, right_param; 183 static char left_buf[248]; 184 static char right_buf[248]; 185 static char info_buf[256]; 186 const char *tmp_name; 187 188 FOR_EACH_MY_SM(link_id, __get_cur_stree(), tmp) { 189 links = tmp->state->data; 190 FOR_EACH_PTR(links, link) { 191 sm = get_sm_state(compare_id, link, NULL); 192 if (!sm) 193 continue; 194 data = sm->state->data; 195 if (!data || !data->comparison) 196 continue; 197 if (ptr_list_size((struct ptr_list *)data->left_vsl) != 1 || 198 ptr_list_size((struct ptr_list *)data->right_vsl) != 1) 199 continue; 200 left = first_ptr_list((struct ptr_list *)data->left_vsl); 201 right = first_ptr_list((struct ptr_list *)data->right_vsl); 202 if (left->sym == right->sym && 203 strcmp(left->var, right->var) == 0) 204 continue; 205 /* 206 * Both parameters link to this comparison so only 207 * record the first one. 208 */ 209 if (left->sym != tmp->sym || 210 strcmp(left->var, tmp->name) != 0) 211 continue; 212 213 left_param = get_param_num_from_sym(left->sym); 214 right_param = get_param_num_from_sym(right->sym); 215 if (left_param < 0 || right_param < 0) /* can't happen hopefully */ 216 continue; 217 218 tmp_name = get_param_name_var_sym(left->var, left->sym); 219 if (!tmp_name) 220 continue; 221 snprintf(left_buf, sizeof(left_buf), "%s", tmp_name); 222 223 tmp_name = get_param_name_var_sym(right->var, right->sym); 224 if (!tmp_name || tmp_name[0] != '$') 225 continue; 226 snprintf(right_buf, sizeof(right_buf), "$%d%s", right_param, tmp_name + 1); 227 228 snprintf(info_buf, sizeof(info_buf), "%s %s", show_special(data->comparison), right_buf); 229 sql_insert_return_states(return_id, return_ranges, 230 COMPARE_LIMIT, left_param, left_buf, info_buf); 231 } END_FOR_EACH_PTR(link); 232 233 } END_FOR_EACH_SM(tmp); 234 } 235 236 static int parse_comparison(char **value, int *op) 237 { 238 239 *op = **value; 240 241 switch (*op) { 242 case '<': 243 (*value)++; 244 if (**value == '=') { 245 (*value)++; 246 *op = SPECIAL_LTE; 247 } 248 break; 249 case '=': 250 (*value)++; 251 (*value)++; 252 *op = SPECIAL_EQUAL; 253 break; 254 case '!': 255 (*value)++; 256 (*value)++; 257 *op = SPECIAL_NOTEQUAL; 258 break; 259 case '>': 260 (*value)++; 261 if (**value == '=') { 262 (*value)++; 263 *op = SPECIAL_GTE; 264 } 265 break; 266 default: 267 return 0; 268 } 269 270 if (**value != ' ') { 271 sm_perror("parsing comparison. %s", *value); 272 return 0; 273 } 274 275 (*value)++; 276 return 1; 277 } 278 279 static int split_op_param_key(char *value, int *op, int *param, char **key) 280 { 281 static char buf[256]; 282 char *p; 283 284 if (!parse_comparison(&value, op)) 285 return 0; 286 287 snprintf(buf, sizeof(buf), "%s", value); 288 289 p = buf; 290 if (*p++ != '$') 291 return 0; 292 293 *param = atoi(p); 294 if (*param < 0 || *param > 99) 295 return 0; 296 p++; 297 if (*param > 9) 298 p++; 299 p--; 300 *p = '$'; 301 *key = p; 302 303 return 1; 304 } 305 306 static void db_return_comparison(struct expression *expr, int left_param, char *key, char *value) 307 { 308 struct expression *left_arg, *right_arg; 309 char *left_name = NULL; 310 struct symbol *left_sym; 311 char *right_name = NULL; 312 struct symbol *right_sym; 313 int op; 314 int right_param; 315 char *right_key; 316 struct var_sym_list *left_vsl = NULL, *right_vsl = NULL; 317 318 while (expr->type == EXPR_ASSIGNMENT) 319 expr = strip_expr(expr->right); 320 if (expr->type != EXPR_CALL) 321 return; 322 323 if (!split_op_param_key(value, &op, &right_param, &right_key)) 324 return; 325 326 left_arg = get_argument_from_call_expr(expr->args, left_param); 327 if (!left_arg) 328 return; 329 330 right_arg = get_argument_from_call_expr(expr->args, right_param); 331 if (!right_arg) 332 return; 333 334 left_name = get_variable_from_key(left_arg, key, &left_sym); 335 if (!left_name || !left_sym) 336 goto free; 337 if (get_param_num_from_sym(left_sym) < 0) 338 goto free; 339 340 right_name = get_variable_from_key(right_arg, right_key, &right_sym); 341 if (!right_name || !right_sym) 342 goto free; 343 if (get_param_num_from_sym(right_sym) < 0) 344 goto free; 345 346 add_var_sym(&left_vsl, left_name, left_sym); 347 add_var_sym(&right_vsl, right_name, right_sym); 348 349 add_comparison_var_sym(left_name, left_vsl, op, right_name, right_vsl); 350 351 free: 352 free_string(left_name); 353 free_string(right_name); 354 } 355 356 void register_param_compare_limit(int id) 357 { 358 compare_id = id; 359 360 set_dynamic_states(compare_id); 361 add_merge_hook(compare_id, &merge_compare_states); 362 add_split_return_callback(&print_return_comparison); 363 364 select_return_states_hook(COMPARE_LIMIT, &db_return_comparison); 365 } 366 367 void register_param_compare_limit_links(int id) 368 { 369 link_id = id; 370 371 set_dynamic_states(link_id); 372 add_merge_hook(link_id, &merge_links); 373 } 374 --- EOF ---