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 /* 134 * This relies on the fact that these states are stored so that 135 * foo->bar is before foo->bar->baz. 136 */ 137 static int parent_set(struct string_list *list, const char *name) 138 { 139 char *tmp; 140 int len; 141 int ret; 142 143 FOR_EACH_PTR(list, tmp) { 144 len = strlen(tmp); 145 ret = strncmp(tmp, name, len); 146 if (ret < 0) 147 continue; 148 if (ret > 0) 149 return 0; 150 if (name[len] == '-') 151 return 1; 152 } END_FOR_EACH_PTR(tmp); 153 154 return 0; 155 } 156 157 static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr) 158 { 159 struct sm_state *sm; 160 struct smatch_state *extra; 161 int param; 162 struct range_list *rl; 163 const char *param_name; 164 struct string_list *set_list = NULL; 165 char *math_str; 166 char buf[256]; 167 sval_t sval; 168 169 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 170 if (!estate_rl(sm->state)) 171 continue; 172 extra = get_state(SMATCH_EXTRA, sm->name, sm->sym); 173 if (extra) { 174 rl = rl_intersection(estate_rl(sm->state), estate_rl(extra)); 175 if (!rl) 176 continue; 177 } else { 178 rl = estate_rl(sm->state); 179 } 180 181 param = get_param_num_from_sym(sm->sym); 182 if (param < 0) 183 continue; 184 param_name = get_param_name(sm); 185 if (!param_name) 186 continue; 187 if (strcmp(param_name, "$") == 0) { 188 insert_string(&set_list, (char *)sm->name); 189 continue; 190 } 191 192 if (rl_to_sval(rl, &sval)) { 193 insert_string(&set_list, (char *)sm->name); 194 sql_insert_return_states(return_id, return_ranges, 195 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET, 196 param, param_name, show_rl(rl)); 197 continue; 198 } 199 200 math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym); 201 if (math_str) { 202 snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str); 203 insert_string(&set_list, (char *)sm->name); 204 sql_insert_return_states(return_id, return_ranges, 205 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET, 206 param, param_name, buf); 207 continue; 208 } 209 210 /* no useful information here. */ 211 if (is_whole_rl(rl) && parent_set(set_list, sm->name)) 212 continue; 213 insert_string(&set_list, (char *)sm->name); 214 215 sql_insert_return_states(return_id, return_ranges, 216 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET, 217 param, param_name, show_rl(rl)); 218 219 } END_FOR_EACH_SM(sm); 220 221 free_ptr_list((struct ptr_list **)&set_list); 222 } 223 224 int param_was_set_var_sym(const char *name, struct symbol *sym) 225 { 226 struct sm_state *sm; 227 int len; 228 229 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) { 230 if (sm->sym != sym) 231 continue; 232 len = strlen(sm->name); 233 if (strncmp(sm->name, name, len) != 0) 234 continue; 235 if (name[len] == '\0' || 236 name[len] == '-') 237 return 1; 238 } END_FOR_EACH_SM(sm); 239 240 return 0; 241 } 242 243 int param_was_set(struct expression *expr) 244 { 245 char *name; 246 struct symbol *sym; 247 int ret = 0; 248 249 name = expr_to_var_sym(expr, &sym); 250 if (!name || !sym) 251 goto free; 252 253 ret = param_was_set_var_sym(name, sym); 254 free: 255 free_string(name); 256 return ret; 257 } 258 259 void register_param_set(int id) 260 { 261 my_id = id; 262 263 add_extra_mod_hook(&extra_mod_hook); 264 add_hook(match_array_assignment, ASSIGNMENT_HOOK); 265 add_unmatched_state_hook(my_id, &unmatched_state); 266 add_merge_hook(my_id, &merge_estates); 267 add_split_return_callback(&print_return_value_param); 268 } 269