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