1 /*
2  * Copyright (C) 2009 Dan Carpenter.
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 /*
19  * There are a number of ways that variables are modified:
20  * 1) assignment
21  * 2) increment/decrement
22  * 3) assembly
23  * 4) inside functions.
24  *
25  * For setting stuff inside a function then, of course, it's more accurate if
26  * you have the cross function database built.  Otherwise we are super
27  * aggressive about marking things as modified and if you have "frob(foo);" then
28  * we assume "foo->bar" is modified.
29  */
30 
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include "smatch.h"
34 #include "smatch_extra.h"
35 #include "smatch_slist.h"
36 
37 enum {
38 	EARLY = 0,
39 	LATE = 1,
40 	BOTH = 2
41 };
42 
43 static modification_hook **hooks;
44 static modification_hook **hooks_late;
45 
46 ALLOCATOR(modification_data, "modification data");
47 
48 static int my_id;
49 static struct smatch_state *alloc_my_state(struct expression *expr, struct smatch_state *prev)
50 {
51 	struct smatch_state *state;
52 	struct modification_data *data;
53 	char *name;
54 
55 	expr = strip_expr(expr);
56 	name = expr_to_str(expr);
57 	if (!name)
58 		return NULL;
59 
60 	state = __alloc_smatch_state(0);
61 	state->name = alloc_sname(name);
62 	free_string(name);
63 
64 	data = __alloc_modification_data(0);
65 	data->prev = prev;
66 	data->cur = expr;
67 	state->data = data;
68 
69 	return state;
70 }
71 
72 void add_modification_hook(int owner, modification_hook *call_back)
73 {
74 	if (hooks[owner])
75 		sm_fatal("multiple modification hooks for %s", check_name(owner));
76 	hooks[owner] = call_back;
77 }
78 
79 void add_modification_hook_late(int owner, modification_hook *call_back)
80 {
81 	if (hooks_late[owner])
82 		sm_fatal("multiple late modification hooks for %s", check_name(owner));
83 	hooks_late[owner] = call_back;
84 }
85 
86 static int matches(char *name, struct symbol *sym, struct sm_state *sm)
87 {
88 	int len;
89 
90 	if (sym != sm->sym)
91 		return false;
92 
93 	len = strlen(name);
94 	if (strncmp(sm->name, name, len) == 0) {
95 		if (sm->name[len] == '\0')
96 			return true;
97 		if (sm->name[len] == '-' || sm->name[len] == '.')
98 			return true;
99 	}
100 	if (sm->name[0] != '*')
101 		return false;
102 	if (strncmp(sm->name + 1, name, len) == 0) {
103 		if (sm->name[len + 1] == '\0')
104 			return true;
105 		if (sm->name[len + 1] == '-' || sm->name[len + 1] == '.')
106 			return true;
107 	}
108 	return false;
109 }
110 
111 static void call_modification_hooks_name_sym(char *name, struct symbol *sym, struct expression *mod_expr, int late)
112 {
113 	struct sm_state *sm;
114 	struct smatch_state *prev;
115 	int match;
116 
117 	prev = get_state(my_id, name, sym);
118 
119 	if (cur_func_sym && !__in_fake_assign)
120 		set_state(my_id, name, sym, alloc_my_state(mod_expr, prev));
121 
122 	FOR_EACH_SM(__get_cur_stree(), sm) {
123 		if (sm->owner > num_checks)
124 			continue;
125 		match = matches(name, sym, sm);
126 		if (!match)
127 			continue;
128 
129 		if (late == EARLY || late == BOTH) {
130 			if (hooks[sm->owner])
131 				(hooks[sm->owner])(sm, mod_expr);
132 		}
133 		if (late == LATE || late == BOTH) {
134 			if (hooks_late[sm->owner])
135 				(hooks_late[sm->owner])(sm, mod_expr);
136 		}
137 
138 	} END_FOR_EACH_SM(sm);
139 }
140 
141 static void call_modification_hooks(struct expression *expr, struct expression *mod_expr, int late)
142 {
143 	char *name;
144 	struct symbol *sym;
145 
146 	if (late == LATE)
147 		update_mtag_data(expr);
148 
149 	name = expr_to_known_chunk_sym(expr, &sym);
150 	if (!name)
151 		goto free;
152 	call_modification_hooks_name_sym(name, sym, mod_expr, late);
153 free:
154 	free_string(name);
155 }
156 
157 static void db_param_add(struct expression *expr, int param, char *key, char *value)
158 {
159 	struct expression *arg, *gen_expr;
160 	char *name, *other_name;
161 	struct symbol *sym, *other_sym;
162 
163 	while (expr->type == EXPR_ASSIGNMENT)
164 		expr = strip_expr(expr->right);
165 	if (expr->type != EXPR_CALL)
166 		return;
167 
168 	arg = get_argument_from_call_expr(expr->args, param);
169 	if (!arg)
170 		return;
171 
172 	gen_expr = gen_expression_from_key(arg, key);
173 	if (gen_expr)
174 		update_mtag_data(gen_expr);
175 
176 	name = get_variable_from_key(arg, key, &sym);
177 	if (!name || !sym)
178 		goto free;
179 
180 	__in_fake_assign++;
181 	call_modification_hooks_name_sym(name, sym, expr, BOTH);
182 	__in_fake_assign--;
183 
184 	other_name = get_other_name_sym(name, sym, &other_sym);
185 	if (other_name) {
186 		__in_fake_assign++;
187 		call_modification_hooks_name_sym(other_name, other_sym, expr, BOTH);
188 		__in_fake_assign--;
189 		free_string(other_name);
190 	}
191 
192 free:
193 	free_string(name);
194 }
195 
196 static void match_assign(struct expression *expr, int late)
197 {
198 	call_modification_hooks(expr->left, expr, late);
199 }
200 
201 static void unop_expr(struct expression *expr, int late)
202 {
203 	if (expr->op != SPECIAL_DECREMENT && expr->op != SPECIAL_INCREMENT)
204 		return;
205 
206 	call_modification_hooks(expr->unop, expr, late);
207 }
208 
209 static void match_call(struct expression *expr)
210 {
211 	struct expression *arg, *tmp;
212 
213 	/* If we have the DB then trust the DB */
214 	if (!option_no_db)
215 		return;
216 
217 	FOR_EACH_PTR(expr->args, arg) {
218 		tmp = strip_expr(arg);
219 		if (tmp->type == EXPR_PREOP && tmp->op == '&')
220 			call_modification_hooks(tmp->unop, expr, BOTH);
221 		else
222 			call_modification_hooks(deref_expression(tmp), expr, BOTH);
223 	} END_FOR_EACH_PTR(arg);
224 }
225 
226 static void asm_expr(struct statement *stmt, int late)
227 {
228 	struct expression *expr;
229 	int state = 0;
230 
231 	FOR_EACH_PTR(stmt->asm_outputs, expr) {
232 		switch (state) {
233 		case 0: /* identifier */
234 		case 1: /* constraint */
235 			state++;
236 			continue;
237 		case 2: /* expression */
238 			state = 0;
239 			call_modification_hooks(expr, NULL, late);
240 			continue;
241 		}
242 	} END_FOR_EACH_PTR(expr);
243 }
244 
245 
246 static void match_assign_early(struct expression *expr)
247 {
248 	match_assign(expr, EARLY);
249 }
250 
251 static void unop_expr_early(struct expression *expr)
252 {
253 	unop_expr(expr, EARLY);
254 }
255 
256 static void asm_expr_early(struct statement *stmt)
257 {
258 	asm_expr(stmt, EARLY);
259 }
260 
261 static void match_assign_late(struct expression *expr)
262 {
263 	match_assign(expr, LATE);
264 }
265 
266 static void unop_expr_late(struct expression *expr)
267 {
268 	unop_expr(expr, LATE);
269 }
270 
271 static void asm_expr_late(struct statement *stmt)
272 {
273 	asm_expr(stmt, LATE);
274 }
275 
276 struct smatch_state *get_modification_state(struct expression *expr)
277 {
278 	return get_state_expr(my_id, expr);
279 }
280 
281 void register_modification_hooks(int id)
282 {
283 	my_id = id;
284 
285 	set_dynamic_states(my_id);
286 
287 	hooks = malloc((num_checks + 1) * sizeof(*hooks));
288 	memset(hooks, 0, (num_checks + 1) * sizeof(*hooks));
289 	hooks_late = malloc((num_checks + 1) * sizeof(*hooks));
290 	memset(hooks_late, 0, (num_checks + 1) * sizeof(*hooks));
291 
292 	add_hook(&match_assign_early, ASSIGNMENT_HOOK);
293 	add_hook(&unop_expr_early, OP_HOOK);
294 	add_hook(&asm_expr_early, ASM_HOOK);
295 }
296 
297 void register_modification_hooks_late(int id)
298 {
299 	add_hook(&match_call, FUNCTION_CALL_HOOK);
300 
301 	select_return_states_hook(PARAM_ADD, &db_param_add);
302 	select_return_states_hook(PARAM_SET, &db_param_add);
303 
304 	add_hook(&match_assign_late, ASSIGNMENT_HOOK_AFTER);
305 	add_hook(&unop_expr_late, OP_HOOK);
306 	add_hook(&asm_expr_late, ASM_HOOK);
307 }
308 
309