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;
alloc_my_state(struct expression * expr,struct smatch_state * prev)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
add_modification_hook(int owner,modification_hook * call_back)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
add_modification_hook_late(int owner,modification_hook * call_back)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
matches(char * name,struct symbol * sym,struct sm_state * sm)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
call_modification_hooks_name_sym(char * name,struct symbol * sym,struct expression * mod_expr,int late)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
call_modification_hooks(struct expression * expr,struct expression * mod_expr,int late)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 name = expr_to_known_chunk_sym(expr, &sym);
147 if (!name)
148 goto free;
149 call_modification_hooks_name_sym(name, sym, mod_expr, late);
150 free:
151 free_string(name);
152 }
153
db_param_add(struct expression * expr,int param,char * key,char * value)154 static void db_param_add(struct expression *expr, int param, char *key, char *value)
155 {
156 struct expression *arg;
157 char *name, *other_name;
158 struct symbol *sym, *other_sym;
159
160 while (expr->type == EXPR_ASSIGNMENT)
161 expr = strip_expr(expr->right);
162 if (expr->type != EXPR_CALL)
163 return;
164
165 arg = get_argument_from_call_expr(expr->args, param);
166 if (!arg)
167 return;
168
169 name = get_variable_from_key(arg, key, &sym);
170 if (!name || !sym)
171 goto free;
172
173 __in_fake_assign++;
174 call_modification_hooks_name_sym(name, sym, expr, BOTH);
175 __in_fake_assign--;
176
177 other_name = get_other_name_sym(name, sym, &other_sym);
178 if (other_name) {
179 __in_fake_assign++;
180 call_modification_hooks_name_sym(other_name, other_sym, expr, BOTH);
181 __in_fake_assign--;
182 free_string(other_name);
183 }
184
185 free:
186 free_string(name);
187 }
188
match_assign(struct expression * expr,int late)189 static void match_assign(struct expression *expr, int late)
190 {
191 call_modification_hooks(expr->left, expr, late);
192 }
193
unop_expr(struct expression * expr,int late)194 static void unop_expr(struct expression *expr, int late)
195 {
196 if (expr->op != SPECIAL_DECREMENT && expr->op != SPECIAL_INCREMENT)
197 return;
198
199 call_modification_hooks(expr->unop, expr, late);
200 }
201
match_call(struct expression * expr)202 static void match_call(struct expression *expr)
203 {
204 struct expression *arg, *tmp;
205
206 /* If we have the DB then trust the DB */
207 if (!option_no_db)
208 return;
209
210 FOR_EACH_PTR(expr->args, arg) {
211 tmp = strip_expr(arg);
212 if (tmp->type == EXPR_PREOP && tmp->op == '&')
213 call_modification_hooks(tmp->unop, expr, BOTH);
214 else
215 call_modification_hooks(deref_expression(tmp), expr, BOTH);
216 } END_FOR_EACH_PTR(arg);
217 }
218
asm_expr(struct statement * stmt,int late)219 static void asm_expr(struct statement *stmt, int late)
220 {
221 struct expression *expr;
222
223 FOR_EACH_PTR(stmt->asm_outputs, expr) {
224 if (expr->type != EXPR_ASM_OPERAND)
225 continue;
226 call_modification_hooks(expr->expr, NULL, late);
227 } END_FOR_EACH_PTR(expr);
228 }
229
match_assign_early(struct expression * expr)230 static void match_assign_early(struct expression *expr)
231 {
232 match_assign(expr, EARLY);
233 }
234
unop_expr_early(struct expression * expr)235 static void unop_expr_early(struct expression *expr)
236 {
237 unop_expr(expr, EARLY);
238 }
239
asm_expr_early(struct statement * stmt)240 static void asm_expr_early(struct statement *stmt)
241 {
242 asm_expr(stmt, EARLY);
243 }
244
match_assign_late(struct expression * expr)245 static void match_assign_late(struct expression *expr)
246 {
247 match_assign(expr, LATE);
248 }
249
unop_expr_late(struct expression * expr)250 static void unop_expr_late(struct expression *expr)
251 {
252 unop_expr(expr, LATE);
253 }
254
asm_expr_late(struct statement * stmt)255 static void asm_expr_late(struct statement *stmt)
256 {
257 asm_expr(stmt, LATE);
258 }
259
get_modification_state(struct expression * expr)260 struct smatch_state *get_modification_state(struct expression *expr)
261 {
262 return get_state_expr(my_id, expr);
263 }
264
register_modification_hooks(int id)265 void register_modification_hooks(int id)
266 {
267 my_id = id;
268
269 set_dynamic_states(my_id);
270
271 hooks = malloc((num_checks + 1) * sizeof(*hooks));
272 memset(hooks, 0, (num_checks + 1) * sizeof(*hooks));
273 hooks_late = malloc((num_checks + 1) * sizeof(*hooks));
274 memset(hooks_late, 0, (num_checks + 1) * sizeof(*hooks));
275
276 add_hook(&match_assign_early, ASSIGNMENT_HOOK);
277 add_hook(&unop_expr_early, OP_HOOK);
278 add_hook(&asm_expr_early, ASM_HOOK);
279 }
280
register_modification_hooks_late(int id)281 void register_modification_hooks_late(int id)
282 {
283 add_hook(&match_call, FUNCTION_CALL_HOOK);
284
285 select_return_states_hook(PARAM_ADD, &db_param_add);
286 select_return_states_hook(PARAM_SET, &db_param_add);
287
288 add_hook(&match_assign_late, ASSIGNMENT_HOOK_AFTER);
289 add_hook(&unop_expr_late, OP_HOOK);
290 add_hook(&asm_expr_late, ASM_HOOK);
291 }
292
293