xref: /illumos-gate/usr/src/tools/smatch/src/check_double_checking.c (revision 1f5207b7604fb44407eb4342aff613f7c4508508)
1*1f5207b7SJohn Levon /*
2*1f5207b7SJohn Levon  * Copyright (C) 2014 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 #define _GNU_SOURCE
19*1f5207b7SJohn Levon #include <string.h>
20*1f5207b7SJohn Levon #include "smatch.h"
21*1f5207b7SJohn Levon #include "smatch_slist.h"
22*1f5207b7SJohn Levon 
23*1f5207b7SJohn Levon static int my_id;
24*1f5207b7SJohn Levon 
25*1f5207b7SJohn Levon STATE(checked);
26*1f5207b7SJohn Levon STATE(modified);
27*1f5207b7SJohn Levon 
28*1f5207b7SJohn Levon struct stree *to_check;
29*1f5207b7SJohn Levon 
30*1f5207b7SJohn Levon static struct statement *get_cur_stmt(void)
31*1f5207b7SJohn Levon {
32*1f5207b7SJohn Levon 	return last_ptr_list((struct ptr_list *)big_statement_stack);
33*1f5207b7SJohn Levon }
34*1f5207b7SJohn Levon 
35*1f5207b7SJohn Levon static void set_modified(struct sm_state *sm, struct expression *mod_expr)
36*1f5207b7SJohn Levon {
37*1f5207b7SJohn Levon 	set_state(my_id, sm->name, sm->sym, &modified);
38*1f5207b7SJohn Levon }
39*1f5207b7SJohn Levon 
40*1f5207b7SJohn Levon static struct expression *strip_condition(struct expression *expr)
41*1f5207b7SJohn Levon {
42*1f5207b7SJohn Levon 	expr = strip_expr(expr);
43*1f5207b7SJohn Levon 
44*1f5207b7SJohn Levon 	if (expr->type == EXPR_PREOP && expr->op == '!')
45*1f5207b7SJohn Levon 		return strip_condition(expr->unop);
46*1f5207b7SJohn Levon 
47*1f5207b7SJohn Levon 	if (expr->type == EXPR_COMPARE &&
48*1f5207b7SJohn Levon 	    (expr->op == SPECIAL_EQUAL ||
49*1f5207b7SJohn Levon 	     expr->op == SPECIAL_NOTEQUAL)) {
50*1f5207b7SJohn Levon 		if (is_zero(expr->left))
51*1f5207b7SJohn Levon 			return strip_condition(expr->right);
52*1f5207b7SJohn Levon 		if (is_zero(expr->right))
53*1f5207b7SJohn Levon 			return strip_condition(expr->left);
54*1f5207b7SJohn Levon 	}
55*1f5207b7SJohn Levon 
56*1f5207b7SJohn Levon 	return expr;
57*1f5207b7SJohn Levon }
58*1f5207b7SJohn Levon 
59*1f5207b7SJohn Levon static int conditions_match(struct expression *cond, struct expression *prev)
60*1f5207b7SJohn Levon {
61*1f5207b7SJohn Levon 	prev = strip_condition(prev);
62*1f5207b7SJohn Levon 
63*1f5207b7SJohn Levon 	if (prev == cond)
64*1f5207b7SJohn Levon 		return 1;
65*1f5207b7SJohn Levon 
66*1f5207b7SJohn Levon 	if (prev->type == EXPR_LOGICAL) {
67*1f5207b7SJohn Levon 		if (conditions_match(cond, prev->left) ||
68*1f5207b7SJohn Levon 		    conditions_match(cond, prev->right))
69*1f5207b7SJohn Levon 			return 1;
70*1f5207b7SJohn Levon 	}
71*1f5207b7SJohn Levon 
72*1f5207b7SJohn Levon 	return 0;
73*1f5207b7SJohn Levon }
74*1f5207b7SJohn Levon 
75*1f5207b7SJohn Levon /*
76*1f5207b7SJohn Levon  * People like to do "if (foo) { ... } else if (!foo) { ... }".  Don't
77*1f5207b7SJohn Levon  * complain when they do that even though it is nonsense.
78*1f5207b7SJohn Levon  */
79*1f5207b7SJohn Levon static int is_obvious_else(struct expression *cond)
80*1f5207b7SJohn Levon {
81*1f5207b7SJohn Levon 	struct statement *parent;
82*1f5207b7SJohn Levon 	struct expression *prev;
83*1f5207b7SJohn Levon 
84*1f5207b7SJohn Levon 	if (!get_cur_stmt())
85*1f5207b7SJohn Levon 		return 0;
86*1f5207b7SJohn Levon 	parent = get_cur_stmt()->parent;
87*1f5207b7SJohn Levon 	if (!parent)
88*1f5207b7SJohn Levon 		return 0;
89*1f5207b7SJohn Levon 
90*1f5207b7SJohn Levon 	if (parent->type != STMT_IF)
91*1f5207b7SJohn Levon 		return 0;
92*1f5207b7SJohn Levon 
93*1f5207b7SJohn Levon 	if (!parent->if_false)
94*1f5207b7SJohn Levon 		return 0;
95*1f5207b7SJohn Levon 	if (parent->if_false != get_cur_stmt())
96*1f5207b7SJohn Levon 		return 0;
97*1f5207b7SJohn Levon 
98*1f5207b7SJohn Levon 	prev = strip_condition(parent->if_conditional);
99*1f5207b7SJohn Levon 
100*1f5207b7SJohn Levon 	return conditions_match(cond, prev);
101*1f5207b7SJohn Levon }
102*1f5207b7SJohn Levon 
103*1f5207b7SJohn Levon static int name_means_synchronize(const char *name)
104*1f5207b7SJohn Levon {
105*1f5207b7SJohn Levon 	if (!name)
106*1f5207b7SJohn Levon 		return 0;
107*1f5207b7SJohn Levon 
108*1f5207b7SJohn Levon 	if (strcasestr(name, "wait"))
109*1f5207b7SJohn Levon 		return 1;
110*1f5207b7SJohn Levon 	if (strcasestr(name, "down"))
111*1f5207b7SJohn Levon 		return 1;
112*1f5207b7SJohn Levon 	if (strcasestr(name, "lock") && !strcasestr(name, "unlock"))
113*1f5207b7SJohn Levon 		return 1;
114*1f5207b7SJohn Levon 	if (strcasestr(name, "delay"))
115*1f5207b7SJohn Levon 		return 1;
116*1f5207b7SJohn Levon 	if (strcasestr(name, "schedule"))
117*1f5207b7SJohn Levon 		return 1;
118*1f5207b7SJohn Levon 	if (strcmp(name, "smp_rmb") == 0)
119*1f5207b7SJohn Levon 		return 1;
120*1f5207b7SJohn Levon 	if (strcmp(name, "mb") == 0)
121*1f5207b7SJohn Levon 		return 1;
122*1f5207b7SJohn Levon 	if (strcmp(name, "barrier") == 0)
123*1f5207b7SJohn Levon 		return 1;
124*1f5207b7SJohn Levon 	return 0;
125*1f5207b7SJohn Levon }
126*1f5207b7SJohn Levon 
127*1f5207b7SJohn Levon static int previous_statement_was_synchronize(void)
128*1f5207b7SJohn Levon {
129*1f5207b7SJohn Levon 	struct statement *stmt;
130*1f5207b7SJohn Levon 	struct position pos;
131*1f5207b7SJohn Levon 	struct position prev_pos;
132*1f5207b7SJohn Levon 	char *ident;
133*1f5207b7SJohn Levon 
134*1f5207b7SJohn Levon 	if (__prev_stmt) {
135*1f5207b7SJohn Levon 		prev_pos = __prev_stmt->pos;
136*1f5207b7SJohn Levon 		prev_pos.line -= 3;
137*1f5207b7SJohn Levon 	} else {
138*1f5207b7SJohn Levon 		prev_pos = __cur_stmt->pos;
139*1f5207b7SJohn Levon 		prev_pos.line -= 5;
140*1f5207b7SJohn Levon 	}
141*1f5207b7SJohn Levon 
142*1f5207b7SJohn Levon 	FOR_EACH_PTR_REVERSE(big_statement_stack, stmt) {
143*1f5207b7SJohn Levon 		if (stmt->pos.line < prev_pos.line)
144*1f5207b7SJohn Levon 			return 0;
145*1f5207b7SJohn Levon 		pos = stmt->pos;
146*1f5207b7SJohn Levon 		ident = get_macro_name(pos);
147*1f5207b7SJohn Levon 		if (name_means_synchronize(ident))
148*1f5207b7SJohn Levon 			return 1;
149*1f5207b7SJohn Levon 		ident = pos_ident(pos);
150*1f5207b7SJohn Levon 		if (!ident)
151*1f5207b7SJohn Levon 			continue;
152*1f5207b7SJohn Levon 		if (strcmp(ident, "if") == 0) {
153*1f5207b7SJohn Levon 			pos.pos += 4;
154*1f5207b7SJohn Levon 			ident = pos_ident(pos);
155*1f5207b7SJohn Levon 			if (!ident)
156*1f5207b7SJohn Levon 				continue;
157*1f5207b7SJohn Levon 		}
158*1f5207b7SJohn Levon 		if (name_means_synchronize(ident))
159*1f5207b7SJohn Levon 			return 1;
160*1f5207b7SJohn Levon 	} END_FOR_EACH_PTR_REVERSE(stmt);
161*1f5207b7SJohn Levon 	return 0;
162*1f5207b7SJohn Levon }
163*1f5207b7SJohn Levon 
164*1f5207b7SJohn Levon static void match_condition(struct expression *expr)
165*1f5207b7SJohn Levon {
166*1f5207b7SJohn Levon 	struct smatch_state *state;
167*1f5207b7SJohn Levon 	sval_t dummy;
168*1f5207b7SJohn Levon 	char *name;
169*1f5207b7SJohn Levon 
170*1f5207b7SJohn Levon 	if (inside_loop())
171*1f5207b7SJohn Levon 		return;
172*1f5207b7SJohn Levon 
173*1f5207b7SJohn Levon 	if (get_value(expr, &dummy))
174*1f5207b7SJohn Levon 		return;
175*1f5207b7SJohn Levon 
176*1f5207b7SJohn Levon 	if (get_macro_name(expr->pos))
177*1f5207b7SJohn Levon 		return;
178*1f5207b7SJohn Levon 
179*1f5207b7SJohn Levon 	state = get_stored_condition(expr);
180*1f5207b7SJohn Levon 	if (!state || !state->data)
181*1f5207b7SJohn Levon 		return;
182*1f5207b7SJohn Levon 	if (get_macro_name(((struct expression *)state->data)->pos))
183*1f5207b7SJohn Levon 		return;
184*1f5207b7SJohn Levon 
185*1f5207b7SJohn Levon 	/*
186*1f5207b7SJohn Levon 	 * we allow double checking for NULL because people do this all the time
187*1f5207b7SJohn Levon 	 * and trying to stop them is a losers' battle.
188*1f5207b7SJohn Levon 	 */
189*1f5207b7SJohn Levon 	if (is_pointer(expr) && implied_condition_true(expr))
190*1f5207b7SJohn Levon 		return;
191*1f5207b7SJohn Levon 
192*1f5207b7SJohn Levon 	if (definitely_inside_loop()) {
193*1f5207b7SJohn Levon 		struct symbol *sym;
194*1f5207b7SJohn Levon 
195*1f5207b7SJohn Levon 		if (__inline_fn)
196*1f5207b7SJohn Levon 			return;
197*1f5207b7SJohn Levon 
198*1f5207b7SJohn Levon 		name = expr_to_var_sym(expr, &sym);
199*1f5207b7SJohn Levon 		if (!name)
200*1f5207b7SJohn Levon 			return;
201*1f5207b7SJohn Levon 		set_state_expr(my_id, expr, &checked);
202*1f5207b7SJohn Levon 		set_state_stree(&to_check, my_id, name, sym, &checked);
203*1f5207b7SJohn Levon 		free_string(name);
204*1f5207b7SJohn Levon 		return;
205*1f5207b7SJohn Levon 	}
206*1f5207b7SJohn Levon 
207*1f5207b7SJohn Levon 	if (is_obvious_else(state->data))
208*1f5207b7SJohn Levon 		return;
209*1f5207b7SJohn Levon 
210*1f5207b7SJohn Levon 	/*
211*1f5207b7SJohn Levon 	 * It's common to test something, then take a lock and test if it is
212*1f5207b7SJohn Levon 	 * still true.
213*1f5207b7SJohn Levon 	 */
214*1f5207b7SJohn Levon 	if (previous_statement_was_synchronize())
215*1f5207b7SJohn Levon 		return;
216*1f5207b7SJohn Levon 
217*1f5207b7SJohn Levon 	name = expr_to_str(expr);
218*1f5207b7SJohn Levon 	sm_warning("we tested '%s' before and it was '%s'", name, state->name);
219*1f5207b7SJohn Levon 	free_string(name);
220*1f5207b7SJohn Levon }
221*1f5207b7SJohn Levon 
222*1f5207b7SJohn Levon int get_check_line(struct sm_state *sm)
223*1f5207b7SJohn Levon {
224*1f5207b7SJohn Levon 	struct sm_state *tmp;
225*1f5207b7SJohn Levon 
226*1f5207b7SJohn Levon 	FOR_EACH_PTR(sm->possible, tmp) {
227*1f5207b7SJohn Levon 		if (tmp->state == &checked)
228*1f5207b7SJohn Levon 			return tmp->line;
229*1f5207b7SJohn Levon 	} END_FOR_EACH_PTR(tmp);
230*1f5207b7SJohn Levon 
231*1f5207b7SJohn Levon 	return get_lineno();
232*1f5207b7SJohn Levon }
233*1f5207b7SJohn Levon 
234*1f5207b7SJohn Levon static void after_loop(struct statement *stmt)
235*1f5207b7SJohn Levon {
236*1f5207b7SJohn Levon 	struct sm_state *check, *sm;
237*1f5207b7SJohn Levon 
238*1f5207b7SJohn Levon 	if (!stmt || stmt->type != STMT_ITERATOR)
239*1f5207b7SJohn Levon 		return;
240*1f5207b7SJohn Levon 	if (definitely_inside_loop())
241*1f5207b7SJohn Levon 		return;
242*1f5207b7SJohn Levon 	if (__inline_fn)
243*1f5207b7SJohn Levon 		return;
244*1f5207b7SJohn Levon 
245*1f5207b7SJohn Levon 	FOR_EACH_SM(to_check, check) {
246*1f5207b7SJohn Levon 		continue;
247*1f5207b7SJohn Levon 		sm = get_sm_state(my_id, check->name, check->sym);
248*1f5207b7SJohn Levon 		continue;
249*1f5207b7SJohn Levon 		if (!sm)
250*1f5207b7SJohn Levon 			continue;
251*1f5207b7SJohn Levon 		if (slist_has_state(sm->possible, &modified))
252*1f5207b7SJohn Levon 			continue;
253*1f5207b7SJohn Levon 
254*1f5207b7SJohn Levon 		sm_printf("%s:%d %s() ", get_filename(), get_check_line(sm), get_function());
255*1f5207b7SJohn Levon 		sm_printf("warn: we tested '%s' already\n", check->name);
256*1f5207b7SJohn Levon 	} END_FOR_EACH_SM(check);
257*1f5207b7SJohn Levon 
258*1f5207b7SJohn Levon 	free_stree(&to_check);
259*1f5207b7SJohn Levon }
260*1f5207b7SJohn Levon 
261*1f5207b7SJohn Levon static void match_func_end(struct symbol *sym)
262*1f5207b7SJohn Levon {
263*1f5207b7SJohn Levon 	if (__inline_fn)
264*1f5207b7SJohn Levon 		return;
265*1f5207b7SJohn Levon 	if (to_check)
266*1f5207b7SJohn Levon 		sm_msg("debug: odd...  found an function without an end.");
267*1f5207b7SJohn Levon 	free_stree(&to_check);
268*1f5207b7SJohn Levon }
269*1f5207b7SJohn Levon 
270*1f5207b7SJohn Levon void check_double_checking(int id)
271*1f5207b7SJohn Levon {
272*1f5207b7SJohn Levon 	my_id = id;
273*1f5207b7SJohn Levon 
274*1f5207b7SJohn Levon 	if (!option_spammy)
275*1f5207b7SJohn Levon 		return;
276*1f5207b7SJohn Levon 
277*1f5207b7SJohn Levon 	add_hook(&match_condition, CONDITION_HOOK);
278*1f5207b7SJohn Levon 	add_modification_hook(my_id, &set_modified);
279*1f5207b7SJohn Levon 	add_hook(after_loop, STMT_HOOK_AFTER);
280*1f5207b7SJohn Levon 	add_hook(&match_func_end, AFTER_FUNC_HOOK);
281*1f5207b7SJohn Levon }
282