1 /* 2 * Copyright (C) 2017 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 smatch_extra.c to use. It sort of like check_assigned_expr.c but 20 * more limited. Say a function returns "64min-s64max[$0->data]" and the caller 21 * does "struct whatever *p = get_data(dev);" then we want to record that p is 22 * now the same as "dev->data". Then if we update "p->foo" it means we can 23 * update "dev->data->foo" as well. 24 * 25 */ 26 27 #include "smatch.h" 28 #include "smatch_slist.h" 29 #include "smatch_extra.h" 30 31 extern int check_assigned_expr_id; 32 static int my_id; 33 static int link_id; 34 35 static struct smatch_state *alloc_my_state(const char *name, struct symbol *sym) 36 { 37 struct smatch_state *state; 38 39 state = __alloc_smatch_state(0); 40 state->name = alloc_sname(name); 41 state->data = sym; 42 return state; 43 } 44 45 static void undef(struct sm_state *sm, struct expression *mod_expr) 46 { 47 if (__in_fake_parameter_assign) 48 return; 49 set_state(my_id, sm->name, sm->sym, &undefined); 50 } 51 52 char *map_call_to_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym) 53 { 54 struct smatch_state *state; 55 int skip; 56 char buf[256]; 57 58 /* skip 'foo->'. This was checked in the caller. */ 59 skip = strlen(sym->ident->name) + 2; 60 61 state = get_state(my_id, sym->ident->name, sym); 62 if (!state || !state->data) 63 return NULL; 64 65 snprintf(buf, sizeof(buf), "%s->%s", state->name, name + skip); 66 *new_sym = state->data; 67 return alloc_string(buf); 68 } 69 70 static char *map_my_state_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack) 71 { 72 int len; 73 char buf[256]; 74 75 if (sm->state->data != sym) 76 return NULL; 77 len = strlen(sm->state->name); 78 if (strncmp(name, sm->state->name, len) != 0) 79 return NULL; 80 81 if (name[len] == '.') 82 return NULL; 83 if (!stack && name[len] != '-') 84 return NULL; 85 snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len); 86 *new_sym = sm->sym; 87 return alloc_string(buf); 88 } 89 90 static char *map_assignment_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack) 91 { 92 struct expression *orig_expr; 93 struct symbol *orig_sym; 94 int len; 95 char buf[256]; 96 97 orig_expr = sm->state->data; 98 if (!orig_expr) 99 return NULL; 100 101 /* 102 * Say we have an assignment like: 103 * foo->bar->my_ptr = my_ptr; 104 * We still expect the function to carry on using "my_ptr" as the 105 * shorter name. That's not a long to short mapping. 106 * 107 */ 108 if (orig_expr->type == EXPR_SYMBOL) 109 return NULL; 110 111 orig_sym = expr_to_sym(orig_expr); 112 if (!orig_sym) 113 return NULL; 114 if (sym != orig_sym) 115 return NULL; 116 117 len = strlen(sm->state->name); 118 if (strncmp(name, sm->state->name, len) != 0) 119 return NULL; 120 121 if (name[len] == '.') 122 return NULL; 123 if (!stack && name[len] != '-') 124 return NULL; 125 snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len); 126 *new_sym = sm->sym; 127 return alloc_string(buf); 128 } 129 130 /* 131 * Normally, we expect people to consistently refer to variables by the shortest 132 * name. So they use "b->a" instead of "foo->bar.a" when both point to the 133 * same memory location. However, when we're dealing across function boundaries 134 * then sometimes we pass frob(foo) which sets foo->bar.a. In that case, we 135 * translate it to the shorter name. Smatch extra updates the shorter name, 136 * which in turn updates the longer name. 137 * 138 */ 139 static char *map_long_to_short_name_sym_helper(const char *name, struct symbol *sym, struct symbol **new_sym, bool stack) 140 { 141 char *ret; 142 struct sm_state *sm; 143 144 *new_sym = NULL; 145 146 FOR_EACH_SM(__get_cur_stree(), sm) { 147 if (sm->owner == my_id) { 148 ret = map_my_state_long_to_short(sm, name, sym, new_sym, stack); 149 if (ret) 150 return ret; 151 continue; 152 } 153 if (sm->owner == check_assigned_expr_id) { 154 ret = map_assignment_long_to_short(sm, name, sym, new_sym, stack); 155 if (ret) 156 return ret; 157 continue; 158 } 159 } END_FOR_EACH_SM(sm); 160 161 return NULL; 162 } 163 164 char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym) 165 { 166 return map_long_to_short_name_sym_helper(name, sym, new_sym, 1); 167 } 168 169 char *map_long_to_short_name_sym_nostack(const char *name, struct symbol *sym, struct symbol **new_sym) 170 { 171 return map_long_to_short_name_sym_helper(name, sym, new_sym, 0); 172 } 173 174 char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym) 175 { 176 char *name; 177 struct symbol *start_sym; 178 struct smatch_state *state; 179 180 *sym = NULL; 181 182 name = expr_to_str_sym(expr, &start_sym); 183 if (!name) 184 return NULL; 185 if (expr->type == EXPR_CALL) 186 start_sym = expr_to_sym(expr->fn); 187 188 state = get_state(my_id, name, start_sym); 189 free_string(name); 190 if (!state || !state->data) 191 return NULL; 192 193 *sym = state->data; 194 return alloc_string(state->name); 195 } 196 197 static void store_mapping_helper(char *left_name, struct symbol *left_sym, struct expression *call, const char *return_string) 198 { 199 const char *p = return_string; 200 char *close; 201 int param; 202 struct expression *arg, *new; 203 char *right_name; 204 struct symbol *right_sym; 205 char buf[256]; 206 207 while (*p && *p != '[') 208 p++; 209 if (!*p) 210 return; 211 p++; 212 if (*p != '$') 213 return; 214 215 snprintf(buf, sizeof(buf), "%s", p); 216 close = strchr(buf, ']'); 217 if (!close) 218 return; 219 *close = '\0'; 220 221 param = atoi(buf + 1); 222 arg = get_argument_from_call_expr(call->args, param); 223 if (!arg) 224 return; 225 226 new = gen_expression_from_key(arg, buf); 227 if (!new) 228 return; 229 230 right_name = expr_to_var_sym(new, &right_sym); 231 if (!right_name || !right_sym) 232 goto free; 233 234 set_state(my_id, left_name, left_sym, alloc_my_state(right_name, right_sym)); 235 store_link(link_id, right_name, right_sym, left_name, left_sym); 236 237 free: 238 free_string(right_name); 239 } 240 241 void __add_return_to_param_mapping(struct expression *expr, const char *return_string) 242 { 243 struct expression *call; 244 char *left_name = NULL; 245 struct symbol *left_sym; 246 247 if (expr->type == EXPR_ASSIGNMENT) { 248 left_name = expr_to_var_sym(expr->left, &left_sym); 249 if (!left_name || !left_sym) 250 goto free; 251 252 call = strip_expr(expr->right); 253 if (call->type != EXPR_CALL) 254 goto free; 255 256 store_mapping_helper(left_name, left_sym, call, return_string); 257 goto free; 258 } 259 260 if (expr->type == EXPR_CALL && 261 expr_get_parent_stmt(expr) && 262 expr_get_parent_stmt(expr)->type == STMT_RETURN) { 263 call = strip_expr(expr); 264 left_sym = expr_to_sym(call->fn); 265 if (!left_sym) 266 return; 267 left_name = expr_to_str(call); 268 if (!left_name) 269 return; 270 271 store_mapping_helper(left_name, left_sym, call, return_string); 272 goto free; 273 274 } 275 276 free: 277 free_string(left_name); 278 } 279 280 void register_return_to_param(int id) 281 { 282 my_id = id; 283 add_modification_hook(my_id, &undef); 284 } 285 286 void register_return_to_param_links(int id) 287 { 288 link_id = id; 289 set_up_link_functions(my_id, link_id); 290 } 291 292