xref: /illumos-gate/usr/src/tools/smatch/src/smatch_param_set.c (revision 1f5207b7604fb44407eb4342aff613f7c4508508)
1*1f5207b7SJohn Levon /*
2*1f5207b7SJohn Levon  * Copyright (C) 2012 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 functions like:
20*1f5207b7SJohn Levon  *
21*1f5207b7SJohn Levon  * int foo(int *x)
22*1f5207b7SJohn Levon  * {
23*1f5207b7SJohn Levon  * 	if (*x == 42) {
24*1f5207b7SJohn Levon  *		*x = 0;
25*1f5207b7SJohn Levon  *		return 1;
26*1f5207b7SJohn Levon  *	}
27*1f5207b7SJohn Levon  * 	return 0;
28*1f5207b7SJohn Levon  * }
29*1f5207b7SJohn Levon  *
30*1f5207b7SJohn Levon  * If we return 1 that means the value of *x has been set to 0.  If we return
31*1f5207b7SJohn Levon  * 0 then we have left *x alone.
32*1f5207b7SJohn Levon  *
33*1f5207b7SJohn Levon  */
34*1f5207b7SJohn Levon 
35*1f5207b7SJohn Levon #include "scope.h"
36*1f5207b7SJohn Levon #include "smatch.h"
37*1f5207b7SJohn Levon #include "smatch_slist.h"
38*1f5207b7SJohn Levon #include "smatch_extra.h"
39*1f5207b7SJohn Levon 
40*1f5207b7SJohn Levon static int my_id;
41*1f5207b7SJohn Levon 
42*1f5207b7SJohn Levon static struct smatch_state *unmatched_state(struct sm_state *sm)
43*1f5207b7SJohn Levon {
44*1f5207b7SJohn Levon 	return alloc_estate_empty();
45*1f5207b7SJohn Levon }
46*1f5207b7SJohn Levon 
47*1f5207b7SJohn Levon static int parent_is_set(const char *name, struct symbol *sym, struct smatch_state *state)
48*1f5207b7SJohn Levon {
49*1f5207b7SJohn Levon 	struct expression *faked;
50*1f5207b7SJohn Levon 	char *left_name;
51*1f5207b7SJohn Levon 	int ret = 0;
52*1f5207b7SJohn Levon 	int len;
53*1f5207b7SJohn Levon 
54*1f5207b7SJohn Levon 	if (!__in_fake_assign)
55*1f5207b7SJohn Levon 		return 0;
56*1f5207b7SJohn Levon 	if (!is_whole_rl(estate_rl(state)))
57*1f5207b7SJohn Levon 		return 0;
58*1f5207b7SJohn Levon 	if (get_state(my_id, name, sym))
59*1f5207b7SJohn Levon 		return 0;
60*1f5207b7SJohn Levon 
61*1f5207b7SJohn Levon 	faked = get_faked_expression();
62*1f5207b7SJohn Levon 	if (!faked)
63*1f5207b7SJohn Levon 		return 0;
64*1f5207b7SJohn Levon 	if ((faked->type == EXPR_PREOP || faked->type == EXPR_POSTOP) &&
65*1f5207b7SJohn Levon 	    (faked->op == SPECIAL_INCREMENT || faked->op == SPECIAL_DECREMENT)) {
66*1f5207b7SJohn Levon 		faked = strip_expr(faked->unop);
67*1f5207b7SJohn Levon 		if (faked->type == EXPR_SYMBOL)
68*1f5207b7SJohn Levon 			return 1;
69*1f5207b7SJohn Levon 		return 0;
70*1f5207b7SJohn Levon 	}
71*1f5207b7SJohn Levon 	if (faked->type != EXPR_ASSIGNMENT)
72*1f5207b7SJohn Levon 		return 0;
73*1f5207b7SJohn Levon 
74*1f5207b7SJohn Levon 	left_name = expr_to_var(faked->left);
75*1f5207b7SJohn Levon 	if (!left_name)
76*1f5207b7SJohn Levon 		return 0;
77*1f5207b7SJohn Levon 
78*1f5207b7SJohn Levon 	len = strlen(left_name);
79*1f5207b7SJohn Levon 	if (strncmp(name, left_name, len) == 0 && name[len] == '-')
80*1f5207b7SJohn Levon 		ret = 1;
81*1f5207b7SJohn Levon 	free_string(left_name);
82*1f5207b7SJohn Levon 
83*1f5207b7SJohn Levon 	return ret;
84*1f5207b7SJohn Levon }
85*1f5207b7SJohn Levon 
86*1f5207b7SJohn Levon static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
87*1f5207b7SJohn Levon {
88*1f5207b7SJohn Levon 	if (parent_is_set(name, sym, state))
89*1f5207b7SJohn Levon 		return;
90*1f5207b7SJohn Levon 	if (get_param_num_from_sym(sym) < 0)
91*1f5207b7SJohn Levon 		return;
92*1f5207b7SJohn Levon 	set_state(my_id, name, sym, state);
93*1f5207b7SJohn Levon }
94*1f5207b7SJohn Levon 
95*1f5207b7SJohn Levon /*
96*1f5207b7SJohn Levon  * This function is is a dirty hack because extra_mod_hook is giving us a NULL
97*1f5207b7SJohn Levon  *  sym instead of a vsl.
98*1f5207b7SJohn Levon  */
99*1f5207b7SJohn Levon static void match_array_assignment(struct expression *expr)
100*1f5207b7SJohn Levon {
101*1f5207b7SJohn Levon 	struct expression *array, *offset;
102*1f5207b7SJohn Levon 	char *name;
103*1f5207b7SJohn Levon 	struct symbol *sym;
104*1f5207b7SJohn Levon 	struct range_list *rl;
105*1f5207b7SJohn Levon 	sval_t sval;
106*1f5207b7SJohn Levon 	char buf[256];
107*1f5207b7SJohn Levon 
108*1f5207b7SJohn Levon 	if (__in_fake_assign)
109*1f5207b7SJohn Levon 		return;
110*1f5207b7SJohn Levon 
111*1f5207b7SJohn Levon 	if (!is_array(expr->left))
112*1f5207b7SJohn Levon 		return;
113*1f5207b7SJohn Levon 	array = get_array_base(expr->left);
114*1f5207b7SJohn Levon 	offset = get_array_offset(expr->left);
115*1f5207b7SJohn Levon 
116*1f5207b7SJohn Levon 	/* These are handled by extra_mod_hook() */
117*1f5207b7SJohn Levon 	if (get_value(offset, &sval))
118*1f5207b7SJohn Levon 		return;
119*1f5207b7SJohn Levon 	name = expr_to_var_sym(array, &sym);
120*1f5207b7SJohn Levon 	if (!name || !sym)
121*1f5207b7SJohn Levon 		goto free;
122*1f5207b7SJohn Levon 	if (get_param_num_from_sym(sym) < 0)
123*1f5207b7SJohn Levon 		goto free;
124*1f5207b7SJohn Levon 	get_absolute_rl(expr->right, &rl);
125*1f5207b7SJohn Levon 	rl = cast_rl(get_type(expr->left), rl);
126*1f5207b7SJohn Levon 
127*1f5207b7SJohn Levon 	snprintf(buf, sizeof(buf), "*%s", name);
128*1f5207b7SJohn Levon 	set_state(my_id, buf, sym, alloc_estate_rl(rl));
129*1f5207b7SJohn Levon free:
130*1f5207b7SJohn Levon 	free_string(name);
131*1f5207b7SJohn Levon }
132*1f5207b7SJohn Levon 
133*1f5207b7SJohn Levon /*
134*1f5207b7SJohn Levon  * This relies on the fact that these states are stored so that
135*1f5207b7SJohn Levon  * foo->bar is before foo->bar->baz.
136*1f5207b7SJohn Levon  */
137*1f5207b7SJohn Levon static int parent_set(struct string_list *list, const char *name)
138*1f5207b7SJohn Levon {
139*1f5207b7SJohn Levon 	char *tmp;
140*1f5207b7SJohn Levon 	int len;
141*1f5207b7SJohn Levon 	int ret;
142*1f5207b7SJohn Levon 
143*1f5207b7SJohn Levon 	FOR_EACH_PTR(list, tmp) {
144*1f5207b7SJohn Levon 		len = strlen(tmp);
145*1f5207b7SJohn Levon 		ret = strncmp(tmp, name, len);
146*1f5207b7SJohn Levon 		if (ret < 0)
147*1f5207b7SJohn Levon 			continue;
148*1f5207b7SJohn Levon 		if (ret > 0)
149*1f5207b7SJohn Levon 			return 0;
150*1f5207b7SJohn Levon 		if (name[len] == '-')
151*1f5207b7SJohn Levon 			return 1;
152*1f5207b7SJohn Levon 	} END_FOR_EACH_PTR(tmp);
153*1f5207b7SJohn Levon 
154*1f5207b7SJohn Levon 	return 0;
155*1f5207b7SJohn Levon }
156*1f5207b7SJohn Levon 
157*1f5207b7SJohn Levon static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
158*1f5207b7SJohn Levon {
159*1f5207b7SJohn Levon 	struct sm_state *sm;
160*1f5207b7SJohn Levon 	struct smatch_state *extra;
161*1f5207b7SJohn Levon 	int param;
162*1f5207b7SJohn Levon 	struct range_list *rl;
163*1f5207b7SJohn Levon 	const char *param_name;
164*1f5207b7SJohn Levon 	struct string_list *set_list = NULL;
165*1f5207b7SJohn Levon 	char *math_str;
166*1f5207b7SJohn Levon 	char buf[256];
167*1f5207b7SJohn Levon 	sval_t sval;
168*1f5207b7SJohn Levon 
169*1f5207b7SJohn Levon 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
170*1f5207b7SJohn Levon 		if (!estate_rl(sm->state))
171*1f5207b7SJohn Levon 			continue;
172*1f5207b7SJohn Levon 		extra = get_state(SMATCH_EXTRA, sm->name, sm->sym);
173*1f5207b7SJohn Levon 		if (extra) {
174*1f5207b7SJohn Levon 			rl = rl_intersection(estate_rl(sm->state), estate_rl(extra));
175*1f5207b7SJohn Levon 			if (!rl)
176*1f5207b7SJohn Levon 				continue;
177*1f5207b7SJohn Levon 		} else {
178*1f5207b7SJohn Levon 			rl = estate_rl(sm->state);
179*1f5207b7SJohn Levon 		}
180*1f5207b7SJohn Levon 
181*1f5207b7SJohn Levon 		param = get_param_num_from_sym(sm->sym);
182*1f5207b7SJohn Levon 		if (param < 0)
183*1f5207b7SJohn Levon 			continue;
184*1f5207b7SJohn Levon 		param_name = get_param_name(sm);
185*1f5207b7SJohn Levon 		if (!param_name)
186*1f5207b7SJohn Levon 			continue;
187*1f5207b7SJohn Levon 		if (strcmp(param_name, "$") == 0) {
188*1f5207b7SJohn Levon 			insert_string(&set_list, (char *)sm->name);
189*1f5207b7SJohn Levon 			continue;
190*1f5207b7SJohn Levon 		}
191*1f5207b7SJohn Levon 
192*1f5207b7SJohn Levon 		if (rl_to_sval(rl, &sval)) {
193*1f5207b7SJohn Levon 			insert_string(&set_list, (char *)sm->name);
194*1f5207b7SJohn Levon 			sql_insert_return_states(return_id, return_ranges,
195*1f5207b7SJohn Levon 					param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
196*1f5207b7SJohn Levon 					param, param_name, show_rl(rl));
197*1f5207b7SJohn Levon 			continue;
198*1f5207b7SJohn Levon 		}
199*1f5207b7SJohn Levon 
200*1f5207b7SJohn Levon 		math_str = get_value_in_terms_of_parameter_math_var_sym(sm->name, sm->sym);
201*1f5207b7SJohn Levon 		if (math_str) {
202*1f5207b7SJohn Levon 			snprintf(buf, sizeof(buf), "%s[%s]", show_rl(rl), math_str);
203*1f5207b7SJohn Levon 			insert_string(&set_list, (char *)sm->name);
204*1f5207b7SJohn Levon 			sql_insert_return_states(return_id, return_ranges,
205*1f5207b7SJohn Levon 					param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
206*1f5207b7SJohn Levon 					param, param_name, buf);
207*1f5207b7SJohn Levon 			continue;
208*1f5207b7SJohn Levon 		}
209*1f5207b7SJohn Levon 
210*1f5207b7SJohn Levon 		/* no useful information here. */
211*1f5207b7SJohn Levon 		if (is_whole_rl(rl) && parent_set(set_list, sm->name))
212*1f5207b7SJohn Levon 			continue;
213*1f5207b7SJohn Levon 		insert_string(&set_list, (char *)sm->name);
214*1f5207b7SJohn Levon 
215*1f5207b7SJohn Levon 		sql_insert_return_states(return_id, return_ranges,
216*1f5207b7SJohn Levon 					 param_has_filter_data(sm) ? PARAM_ADD : PARAM_SET,
217*1f5207b7SJohn Levon 					 param, param_name, show_rl(rl));
218*1f5207b7SJohn Levon 
219*1f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
220*1f5207b7SJohn Levon 
221*1f5207b7SJohn Levon 	free_ptr_list((struct ptr_list **)&set_list);
222*1f5207b7SJohn Levon }
223*1f5207b7SJohn Levon 
224*1f5207b7SJohn Levon int param_was_set_var_sym(const char *name, struct symbol *sym)
225*1f5207b7SJohn Levon {
226*1f5207b7SJohn Levon 	struct sm_state *sm;
227*1f5207b7SJohn Levon 	int len;
228*1f5207b7SJohn Levon 
229*1f5207b7SJohn Levon 	FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
230*1f5207b7SJohn Levon 		if (sm->sym != sym)
231*1f5207b7SJohn Levon 			continue;
232*1f5207b7SJohn Levon 		len = strlen(sm->name);
233*1f5207b7SJohn Levon 		if (strncmp(sm->name, name, len) != 0)
234*1f5207b7SJohn Levon 			continue;
235*1f5207b7SJohn Levon 		if (name[len] == '\0' ||
236*1f5207b7SJohn Levon 		    name[len] == '-')
237*1f5207b7SJohn Levon 			return 1;
238*1f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
239*1f5207b7SJohn Levon 
240*1f5207b7SJohn Levon 	return 0;
241*1f5207b7SJohn Levon }
242*1f5207b7SJohn Levon 
243*1f5207b7SJohn Levon int param_was_set(struct expression *expr)
244*1f5207b7SJohn Levon {
245*1f5207b7SJohn Levon 	char *name;
246*1f5207b7SJohn Levon 	struct symbol *sym;
247*1f5207b7SJohn Levon 	int ret = 0;
248*1f5207b7SJohn Levon 
249*1f5207b7SJohn Levon 	name = expr_to_var_sym(expr, &sym);
250*1f5207b7SJohn Levon 	if (!name || !sym)
251*1f5207b7SJohn Levon 		goto free;
252*1f5207b7SJohn Levon 
253*1f5207b7SJohn Levon 	ret = param_was_set_var_sym(name, sym);
254*1f5207b7SJohn Levon free:
255*1f5207b7SJohn Levon 	free_string(name);
256*1f5207b7SJohn Levon 	return ret;
257*1f5207b7SJohn Levon }
258*1f5207b7SJohn Levon 
259*1f5207b7SJohn Levon void register_param_set(int id)
260*1f5207b7SJohn Levon {
261*1f5207b7SJohn Levon 	my_id = id;
262*1f5207b7SJohn Levon 
263*1f5207b7SJohn Levon 	add_extra_mod_hook(&extra_mod_hook);
264*1f5207b7SJohn Levon 	add_hook(match_array_assignment, ASSIGNMENT_HOOK);
265*1f5207b7SJohn Levon 	add_unmatched_state_hook(my_id, &unmatched_state);
266*1f5207b7SJohn Levon 	add_merge_hook(my_id, &merge_estates);
267*1f5207b7SJohn Levon 	add_split_return_callback(&print_return_value_param);
268*1f5207b7SJohn Levon }
269*1f5207b7SJohn Levon 
270