1 /* 2 * Copyright (C) 2012 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 * This is for functions like: 20 * 21 * int foo(int *x) 22 * { 23 * if (*x == 42) { 24 * *x = 0; 25 * return 1; 26 * } 27 * return 0; 28 * } 29 * 30 * If we return 1 that means the value of *x has been set to 0. If we return 31 * 0 then we have left *x alone. 32 * 33 */ 34 35 #include "scope.h" 36 #include "smatch.h" 37 #include "smatch_slist.h" 38 #include "smatch_extra.h" 39 40 static int my_id; 41 42 static struct smatch_state *unmatched_state(struct sm_state *sm) 43 { 44 return alloc_estate_empty(); 45 } 46 47 static int parent_is_set(const char *name, struct symbol *sym, struct smatch_state *state) 48 { 49 struct expression *faked; 50 char *left_name; 51 int ret = 0; 52 int len; 53 54 if (!__in_fake_assign) 55 return 0; 56 if (!is_whole_rl(estate_rl(state))) 57 return 0; 58 if (get_state(my_id, name, sym)) 59 return 0; 60 61 faked = get_faked_expression(); 62 if (!faked) 63 return 0; 64 if ((faked->type == EXPR_PREOP || faked->type == EXPR_POSTOP) && 65 (faked->op == SPECIAL_INCREMENT || faked->op == SPECIAL_DECREMENT)) { 66 faked = strip_expr(faked->unop); 67 if (faked->type == EXPR_SYMBOL) 68 return 1; 69 return 0; 70 } 71 if (faked->type != EXPR_ASSIGNMENT) 72 return 0; 73 74 left_name = expr_to_var(faked->left); 75 if (!left_name) 76 return 0; 77 78 len = strlen(left_name); 79 if (strncmp(name, left_name, len) == 0 && name[len] == '-') 80 ret = 1; 81 free_string(left_name); 82 83 return ret; 84 } 85 86 static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state) 87 { 88 if (parent_is_set(name, sym, state)) 89 return; 90 if (get_param_num_from_sym(sym) < 0) 91 return; 92 set_state(my_id, name, sym, state); 93 } 94 95 /* 96 * This function is is a dirty hack because extra_mod_hook is giving us a NULL 97 * sym instead of a vsl. 98 */ 99 static void match_array_assignment(struct expression *expr) 100 { 101 struct expression *array, *offset; 102 char *name; 103 struct symbol *sym; 104 struct range_list *rl; 105 sval_t sval; 106 char buf[256]; 107 108 if (__in_fake_assign) 109 return; 110 111 if (!is_array(expr->left)) 112 return; 113 array = get_array_base(expr->left); 114 offset = get_array_offset(expr->left); 115 116 /* These are handled by extra_mod_hook() */ 117 if (get_value(offset, &sval)) 118 return; 119 name = expr_to_var_sym(array, &sym); 120 if (!name || !sym) 121 goto free; 122 if (get_param_num_from_sym(sym) < 0) 123 goto free; 124 get_absolute_rl(expr->right, &rl); 125 rl = cast_rl(get_type(expr->left), rl); 126 127 snprintf(buf, sizeof(buf), "*%s", name); 128 set_state(my_id, buf, sym, alloc_estate_rl(rl)); 129 free: 130 free_string(name); 131 } 132 133 static char *get_two_dots(const char *name) 134 { 135 static char buf[80]; 136 int i, cnt = 0; 137 138 for (i = 0; i < sizeof(buf); i++) { 139 if (name[i] == '.') { 140 cnt++; 141 if (cnt >= 2) { 142 buf[i] = '\0'; 143 return buf; 144 } 145 } 146 buf[i] = name[i]; 147 } 148 return NULL; 149 } 150 151 /* 152 * This relies on the fact that these states are stored so that 153 * foo->bar is before foo->bar->baz. 154 */ 155 static int parent_set(struct string_list *list, const char *name) 156 { 157 char *tmp; 158 int len; 159 int ret; 160 161 FOR_EACH_PTR(list, tmp) { 162 len = strlen(tmp); 163 ret = strncmp(tmp, name, len); 164 if (ret < 0) 165 continue; 166 if (ret > 0) 167 return 0; 168 if (name[len] == '-') 169 return 1; 170 } END_FOR_EACH_PTR(tmp); 171 172 return 0; 173 } 174 175 static void print_return_value_param_helper(int return_id, char *return_ranges, struct expression *expr, int limit) 176 { 177 struct sm_state *sm; 178 struct smatch_state *extra; 179 int param; 180 struct range_list *rl; 181 const char *param_name; 182 struct string_list *set_list = NULL; 183 char *math_str; 184 char buf[256]; 185 char two_dot[80] = ""; 186 int count = 0; 187 188 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 189 if (!estate_rl(sm->state)) 190 continue; 191 extra = __get_state(SMATCH_EXTRA, sm->name, sm->sym); 192 if (extra) { 193 rl = rl_intersection(estate_rl(sm->state), estate_rl(extra)); 194 if (!rl) 195 continue; 196 } else { 197 rl = estate_rl(sm->state); 198 } 199 200 param = get_param_num_from_sym(sm->sym); 201 if (param < 0) 202 continue; 203 param_name = get_param_name(sm); 204 if (!param_name) 205 continue; 206 if (strcmp(param_name, "$") == 0) { 207 insert_string(&set_list, (char *)sm->name); 208 continue; 209 } 210 if (is_recursive_member(param_name)) { 211 insert_string(&set_list, (char *)sm->name); 212 continue; 213 } 214 215 if (is_ignored_kernel_data(param_name)) { 216 insert_string(&set_list, (char *)sm->name); 217 continue; 218 } 219 if (limit) { 220 char *new = get_two_dots(param_name); 221 222 if (new) { 223 if (strcmp(new, two_dot) == 0) 224 continue; 225 strncpy(two_dot, new, sizeof(two_dot)); 226 sql_insert_return_states(return_id, return_ranges, 227 PARAM_SET, param, new, "s64min-s64max"); 228 continue; 229 } 230 } 231 232 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym); 233 if (math_str) { 234 snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str); 235 insert_string(&set_list, (char *)sm->name); 236 sql_insert_return_states(return_id, return_ranges, 237 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET, 238 param, param_name, buf); 239 continue; 240 } 241 242 /* no useful information here. */ 243 if (is_whole_rl(rl) && parent_set(set_list, sm->name)) 244 continue; 245 insert_string(&set_list, (char *)sm->name); 246 247 sql_insert_return_states(return_id, return_ranges, 248 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET, 249 param, param_name, show_rl(rl)); 250 if (limit && ++count > limit) 251 break; 252 253 } END_FOR_EACH_SM(sm); 254 255 free_ptr_list((struct ptr_list **)&set_list); 256 } 257 258 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr) 259 { 260 print_return_value_param_helper(return_id, return_ranges, expr, 0); 261 } 262 263 void print_limited_param_set(int return_id, char *return_ranges, struct expression *expr) 264 { 265 print_return_value_param_helper(return_id, return_ranges, expr, 1000); 266 } 267 268 static int possibly_empty(struct sm_state *sm) 269 { 270 struct sm_state *tmp; 271 272 FOR_EACH_PTR(sm->possible, tmp) { 273 if (strcmp(tmp->name, "") == 0) 274 return 1; 275 } END_FOR_EACH_PTR(tmp); 276 return 0; 277 } 278 279 int param_was_set_var_sym(const char *name, struct symbol *sym) 280 { 281 struct sm_state *sm; 282 char buf[80]; 283 int len, i; 284 285 if (!name) 286 return 0; 287 288 len = strlen(name); 289 if (len >= sizeof(buf)) 290 len = sizeof(buf) - 1; 291 292 for (i = 0; i <= len; i++) { 293 if (name[i] != '-' && name[i] != '\0') 294 continue; 295 296 memcpy(buf, name, i); 297 buf[i] = '\0'; 298 299 sm = get_sm_state(my_id, buf, sym); 300 if (!sm) 301 continue; 302 if (possibly_empty(sm)) 303 continue; 304 return 1; 305 } 306 307 if (name[0] == '*') 308 return param_was_set_var_sym(name + 1, sym); 309 310 return 0; 311 } 312 313 int param_was_set(struct expression *expr) 314 { 315 char *name; 316 struct symbol *sym; 317 int ret = 0; 318 319 name = expr_to_var_sym(expr, &sym); 320 if (!name || !sym) 321 goto free; 322 323 ret = param_was_set_var_sym(name, sym); 324 free: 325 free_string(name); 326 return ret; 327 } 328 329 void register_param_set(int id) 330 { 331 my_id = id; 332 333 set_dynamic_states(my_id); 334 add_extra_mod_hook(&extra_mod_hook); 335 add_hook(match_array_assignment, ASSIGNMENT_HOOK); 336 add_unmatched_state_hook(my_id, &unmatched_state); 337 add_merge_hook(my_id, &merge_estates); 338 add_split_return_callback(&print_return_value_param); 339 } 340