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 = sym->ident->len + 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 /* 91 * Normally, we expect people to consistently refer to variables by the shortest 92 * name. So they use "b->a" instead of "foo->bar.a" when both point to the 93 * same memory location. However, when we're dealing across function boundaries 94 * then sometimes we pass frob(foo) which sets foo->bar.a. In that case, we 95 * translate it to the shorter name. Smatch extra updates the shorter name, 96 * which in turn updates the longer name. 97 * 98 */ 99 char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym, bool use_stack) 100 { 101 char *ret; 102 struct sm_state *sm; 103 104 *new_sym = NULL; 105 106 FOR_EACH_SM(__get_cur_stree(), sm) { 107 if (sm->owner == my_id) { 108 ret = map_my_state_long_to_short(sm, name, sym, new_sym, use_stack); 109 if (ret) { 110 if (local_debug) 111 sm_msg("%s: my_state: name = '%s' sm = '%s'", 112 __func__, name, show_sm(sm)); 113 return ret; 114 } 115 continue; 116 } 117 } END_FOR_EACH_SM(sm); 118 119 return NULL; 120 } 121 122 char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym) 123 { 124 char *name; 125 struct symbol *start_sym; 126 struct smatch_state *state; 127 128 *sym = NULL; 129 130 name = expr_to_str_sym(expr, &start_sym); 131 if (!name) 132 return NULL; 133 if (expr->type == EXPR_CALL) 134 start_sym = expr_to_sym(expr->fn); 135 136 state = get_state(my_id, name, start_sym); 137 free_string(name); 138 if (!state || !state->data) 139 return NULL; 140 141 *sym = state->data; 142 return alloc_string(state->name); 143 } 144 145 static void store_mapping_helper(char *left_name, struct symbol *left_sym, struct expression *call, const char *return_string) 146 { 147 const char *p = return_string; 148 char *close; 149 int param; 150 struct expression *arg, *new; 151 char *right_name; 152 struct symbol *right_sym; 153 char buf[256]; 154 155 while (*p && *p != '[') 156 p++; 157 if (!*p) 158 return; 159 p++; 160 if (*p != '$') 161 return; 162 163 snprintf(buf, sizeof(buf), "%s", p); 164 close = strchr(buf, ']'); 165 if (!close) 166 return; 167 *close = '\0'; 168 169 param = atoi(buf + 1); 170 arg = get_argument_from_call_expr(call->args, param); 171 if (!arg) 172 return; 173 174 new = gen_expression_from_key(arg, buf); 175 if (!new) 176 return; 177 178 right_name = expr_to_var_sym(new, &right_sym); 179 if (!right_name || !right_sym) 180 goto free; 181 182 set_state(my_id, left_name, left_sym, alloc_my_state(right_name, right_sym)); 183 store_link(link_id, right_name, right_sym, left_name, left_sym); 184 185 free: 186 free_string(right_name); 187 } 188 189 void __add_return_to_param_mapping(struct expression *expr, const char *return_string) 190 { 191 struct expression *call; 192 char *left_name = NULL; 193 struct symbol *left_sym; 194 195 if (expr->type == EXPR_ASSIGNMENT) { 196 left_name = expr_to_var_sym(expr->left, &left_sym); 197 if (!left_name || !left_sym) 198 goto free; 199 200 call = strip_expr(expr->right); 201 if (call->type != EXPR_CALL) 202 goto free; 203 204 store_mapping_helper(left_name, left_sym, call, return_string); 205 goto free; 206 } 207 208 if (expr->type == EXPR_CALL && 209 expr_get_parent_stmt(expr) && 210 expr_get_parent_stmt(expr)->type == STMT_RETURN) { 211 call = strip_expr(expr); 212 left_sym = expr_to_sym(call->fn); 213 if (!left_sym) 214 return; 215 left_name = expr_to_str(call); 216 if (!left_name) 217 return; 218 219 store_mapping_helper(left_name, left_sym, call, return_string); 220 goto free; 221 222 } 223 224 free: 225 free_string(left_name); 226 } 227 228 void register_return_to_param(int id) 229 { 230 my_id = id; 231 set_dynamic_states(my_id); 232 add_modification_hook(my_id, &undef); 233 } 234 235 void register_return_to_param_links(int id) 236 { 237 link_id = id; 238 set_up_link_functions(my_id, link_id); 239 } 240