return_to_param: don't modify memory on the stack
[smatch.git] / smatch_return_to_param.c
blob5a6b9748814dfde5d2203c4ca758ffb9c9cf94a7
1 /*
2 * Copyright (C) 2017 Oracle.
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.
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.
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
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.
27 #include "smatch.h"
28 #include "smatch_slist.h"
29 #include "smatch_extra.h"
31 extern int check_assigned_expr_id;
32 static int my_id;
33 static int link_id;
35 static struct smatch_state *alloc_my_state(const char *name, struct symbol *sym)
37 struct smatch_state *state;
39 state = __alloc_smatch_state(0);
40 state->name = alloc_sname(name);
41 state->data = sym;
42 return state;
45 static void undef(struct sm_state *sm, struct expression *mod_expr)
47 set_state(my_id, sm->name, sm->sym, &undefined);
50 char *map_call_to_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
52 struct smatch_state *state;
53 int skip;
54 char buf[256];
56 /* skip 'foo->'. This was checked in the caller. */
57 skip = strlen(sym->ident->name) + 2;
59 state = get_state(my_id, sym->ident->name, sym);
60 if (!state || !state->data)
61 return NULL;
63 snprintf(buf, sizeof(buf), "%s->%s", state->name, name + skip);
64 *new_sym = state->data;
65 return alloc_string(buf);
68 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 int len;
71 char buf[256];
73 if (sm->state->data != sym)
74 return NULL;
75 len = strlen(sm->state->name);
76 if (strncmp(name, sm->state->name, len) != 0)
77 return NULL;
79 if (name[len] == '.')
80 return NULL;
81 if (!stack && name[len] != '-')
82 return NULL;
83 snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len);
84 *new_sym = sm->sym;
85 return alloc_string(buf);
88 static char *map_assignment_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
90 struct symbol *orig_sym;
91 int len;
92 char buf[256];
94 if (!sm->state->data)
95 return NULL;
97 orig_sym = expr_to_sym(sm->state->data);
98 if (!orig_sym)
99 return NULL;
100 if (sym != orig_sym)
101 return NULL;
103 len = strlen(sm->state->name);
104 if (strncmp(name, sm->state->name, len) != 0)
105 return NULL;
107 if (name[len] == '.')
108 return NULL;
109 if (!stack && name[len] != '-')
110 return NULL;
111 snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len);
112 *new_sym = sm->sym;
113 return alloc_string(buf);
117 * Normally, we expect people to consistently refer to variables by the shortest
118 * name. So they use "b->a" instead of "foo->bar.a" when both point to the
119 * same memory location. However, when we're dealing across function boundaries
120 * then sometimes we pass frob(foo) which sets foo->bar.a. In that case, we
121 * translate it to the shorter name. Smatch extra updates the shorter name,
122 * which in turn updates the longer name.
125 static char *map_long_to_short_name_sym_helper(const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
127 char *ret;
128 struct sm_state *sm;
130 *new_sym = NULL;
132 FOR_EACH_SM(__get_cur_stree(), sm) {
133 if (sm->owner == my_id) {
134 ret = map_my_state_long_to_short(sm, name, sym, new_sym, stack);
135 if (ret)
136 return ret;
137 continue;
139 if (sm->owner == check_assigned_expr_id) {
140 ret = map_assignment_long_to_short(sm, name, sym, new_sym, stack);
141 if (ret)
142 return ret;
143 continue;
145 } END_FOR_EACH_SM(sm);
147 return NULL;
150 char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
152 return map_long_to_short_name_sym_helper(name, sym, new_sym, 1);
155 char *map_long_to_short_name_sym_nostack(const char *name, struct symbol *sym, struct symbol **new_sym)
157 return map_long_to_short_name_sym_helper(name, sym, new_sym, 0);
160 char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym)
162 char *name;
163 struct smatch_state *state;
165 *sym = NULL;
167 name = expr_to_str(expr);
168 if (!name)
169 return NULL;
170 state = get_state(my_id, name, (struct symbol *)expr);
171 if (!state || !state->data)
172 return NULL;
173 *sym = state->data;
174 return alloc_string(state->name);
177 static void store_mapping_helper(char *left_name, struct symbol *left_sym, struct expression *call, const char *return_string)
179 const char *p = return_string;
180 const char *end;
181 int param;
182 struct expression *arg;
183 char *name = NULL;
184 struct symbol *right_sym;
185 char buf[256];
187 while (*p && *p != '[')
188 p++;
189 if (!*p)
190 goto free;
191 p++;
192 if (*p != '$')
193 goto free;
194 p++;
195 param = atoi(p);
196 p++;
197 if (*p != '-' || *(p + 1) != '>')
198 goto free;
199 end = strchr(p, ']');
200 if (!end)
201 goto free;
203 arg = get_argument_from_call_expr(call->args, param);
204 if (!arg)
205 goto free;
206 name = expr_to_var_sym(arg, &right_sym);
207 if (!name || !right_sym)
208 goto free;
209 snprintf(buf, sizeof(buf), "%s", name);
211 if (end - p + strlen(buf) >= sizeof(buf))
212 goto free;
213 strncat(buf, p, end - p);
215 set_state(my_id, left_name, left_sym, alloc_my_state(buf, right_sym));
216 //set_state(my_id, buf, right_sym, alloc_my_state(left_name, left_sym));
218 store_link(link_id, buf, right_sym, left_name, left_sym);
220 free:
221 free_string(name);
224 void __add_return_to_param_mapping(struct expression *expr, const char *return_string)
226 struct expression *call;
227 char *left_name = NULL;
228 struct symbol *left_sym;
230 if (expr->type == EXPR_ASSIGNMENT) {
231 left_name = expr_to_var_sym(expr->left, &left_sym);
232 if (!left_name || !left_sym)
233 goto free;
235 call = strip_expr(expr->right);
236 if (call->type != EXPR_CALL)
237 goto free;
239 store_mapping_helper(left_name, left_sym, call, return_string);
240 goto free;
243 if (expr->type == EXPR_CALL &&
244 expr_get_parent_stmt(expr) &&
245 expr_get_parent_stmt(expr)->type == STMT_RETURN) {
246 call = strip_expr(expr);
247 left_name = expr_to_str(call);
248 if (!left_name)
249 goto free;
252 * HACK ALERT: The symbol pointer is basically used as a cookie
253 * and not used as a pointer so we can pass expr here without
254 * causing an issue.
257 store_mapping_helper(left_name, (struct symbol *)expr, call, return_string);
258 goto free;
262 free:
263 free_string(left_name);
266 void register_return_to_param(int id)
268 my_id = id;
269 add_modification_hook(my_id, &undef);
272 void register_return_to_param_links(int id)
274 link_id = id;
275 set_up_link_functions(my_id, link_id);