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