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