validation/sm_compare12.c: update to reflect latest code
[smatch.git] / smatch_struct_assignment.c
blobc734f5925312a1fa50a254d8968b2229c034c14c
1 /*
2 * Copyright (C) 2014 Oracle.
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.
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.
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
19 * This file started out by saying that if you have:
21 * struct foo one, two;
22 * ...
23 * one = two;
25 * That's equivalent to saying:
27 * one.x = two.x;
28 * one.y = two.y;
30 * Turning an assignment like that into a bunch of small fake assignments is
31 * really useful.
33 * The call to memcpy(&one, &two, sizeof(foo)); is the same as "one = two;" so
34 * we can re-use the code. And we may as well use it for memset() too.
35 * Assigning pointers is almost the same:
37 * p1 = p2;
39 * Is the same as:
41 * p1->x = p2->x;
42 * p1->y = p2->y;
44 * The problem is that you can go a bit crazy with pointers to pointers.
46 * p1->x->y->z->one->two->three = p2->x->y->z->one->two->three;
48 * I don't have a proper solution for this problem right now. I just copy one
49 * level and don't nest. It should handle limitted nesting but intelligently.
51 * The other thing is that you end up with a lot of garbage assignments where
52 * we record "x could be anything. x->y could be anything. x->y->z->a->b->c
53 * could *also* be anything!". There should be a better way to filter this
54 * useless information.
58 #include "scope.h"
59 #include "smatch.h"
60 #include "smatch_slist.h"
61 #include "smatch_extra.h"
63 enum {
64 COPY_NORMAL,
65 COPY_MEMCPY,
66 COPY_MEMSET,
69 static struct symbol *get_struct_type(struct expression *expr)
71 struct symbol *type;
73 type = get_type(expr);
74 if (!type)
75 return NULL;
76 if (type->type == SYM_PTR)
77 type = get_real_base_type(type);
78 if (type && type->type == SYM_STRUCT)
79 return type;
80 return NULL;
83 static struct expression *get_right_base_expr(struct symbol *left_type, struct expression *right)
85 struct symbol *struct_type;
87 if (!right)
88 return NULL;
90 struct_type = get_struct_type(right);
91 if (!struct_type)
92 return NULL;
93 if (struct_type != left_type)
94 return NULL;
96 if (right->type == EXPR_PREOP && right->op == '&')
97 right = strip_expr(right->unop);
99 if (right->type == EXPR_CALL)
100 return NULL;
102 if (is_pointer(right))
103 right = deref_expression(right);
105 return right;
108 static struct expression *remove_addr(struct expression *expr)
110 struct symbol *type;
112 expr = strip_expr(expr);
114 if (expr->type == EXPR_PREOP && expr->op == '&')
115 return strip_expr(expr->unop);
116 type = get_type(expr);
117 if (!type || type->type != SYM_PTR)
118 return expr;
120 return deref_expression(expr);
123 static struct expression *faked_expression;
124 struct expression *get_faked_expression(void)
126 if (!__in_fake_assign)
127 return NULL;
128 return faked_expression;
131 static void split_fake_expr(struct expression *expr)
133 __in_fake_assign++;
134 __split_expr(expr);
135 __in_fake_assign--;
138 static void set_inner_struct_members(int mode, struct expression *faked, struct expression *left, struct expression *right, struct symbol *member)
140 struct expression *left_member;
141 struct expression *right_member = NULL; /* silence GCC */
142 struct expression *assign;
143 struct symbol *base = get_real_base_type(member);
144 struct symbol *tmp;
146 if (member->ident) {
147 left = member_expression(left, '.', member->ident);
148 if (mode != COPY_MEMSET && right)
149 right = member_expression(right, '.', member->ident);
152 FOR_EACH_PTR(base->symbol_list, tmp) {
153 struct symbol *type;
155 type = get_real_base_type(tmp);
156 if (!type)
157 continue;
159 if (type->type == SYM_ARRAY)
160 continue;
161 if (type->type == SYM_UNION || type->type == SYM_STRUCT) {
162 set_inner_struct_members(mode, faked, left, right, tmp);
163 continue;
165 if (!tmp->ident)
166 continue;
168 left_member = member_expression(left, '.', tmp->ident);
170 switch (mode) {
171 case COPY_NORMAL:
172 case COPY_MEMCPY:
173 if (right)
174 right_member = member_expression(right, '.', tmp->ident);
175 else
176 right_member = unknown_value_expression(left_member);
177 break;
178 case COPY_MEMSET:
179 right_member = right;
180 break;
183 assign = assign_expression(left_member, right_member);
184 split_fake_expr(assign);
185 } END_FOR_EACH_PTR(tmp);
188 static void __struct_members_copy(int mode, struct expression *faked,
189 struct expression *left,
190 struct expression *right)
192 struct symbol *struct_type, *tmp, *type;
193 struct expression *left_member;
194 struct expression *right_member;
195 struct expression *assign;
196 int op = '.';
199 if (__in_fake_assign)
200 return;
201 faked_expression = faked;
203 left = strip_expr(left);
204 right = strip_expr(right);
206 struct_type = get_struct_type(left);
207 if (!struct_type) {
209 * This is not a struct assignment obviously. But this is where
210 * memcpy() is handled so it feels like a good place to add this
211 * code.
214 type = get_type(left);
215 if (!type || type->type != SYM_BASETYPE)
216 goto done;
218 right = strip_expr(right);
219 if (right && right->type == EXPR_PREOP && right->op == '&')
220 right = remove_addr(right);
221 else
222 right = unknown_value_expression(left);
223 assign = assign_expression(left, right);
224 split_fake_expr(assign);
225 goto done;
228 if (is_pointer(left)) {
229 left = deref_expression(left);
230 op = '*';
232 if (mode != COPY_MEMSET)
233 right = get_right_base_expr(struct_type, right);
235 FOR_EACH_PTR(struct_type->symbol_list, tmp) {
236 type = get_real_base_type(tmp);
237 if (!type)
238 continue;
239 if (type->type == SYM_ARRAY)
240 continue;
242 if (type->type == SYM_UNION || type->type == SYM_STRUCT) {
243 set_inner_struct_members(mode, faked, left, right, tmp);
244 continue;
247 if (!tmp->ident)
248 continue;
250 left_member = member_expression(left, op, tmp->ident);
251 right_member = NULL;
253 switch (mode) {
254 case COPY_NORMAL:
255 case COPY_MEMCPY:
256 if (right)
257 right_member = member_expression(right, op, tmp->ident);
258 else
259 right_member = unknown_value_expression(left_member);
260 break;
261 case COPY_MEMSET:
262 right_member = right;
263 break;
265 if (!right_member) {
266 sm_msg("internal. No right member");
267 continue;
269 assign = assign_expression(left_member, right_member);
270 split_fake_expr(assign);
271 } END_FOR_EACH_PTR(tmp);
273 done:
274 faked_expression = NULL;
277 static int returns_zeroed_mem(struct expression *expr)
279 char *fn;
281 if (expr->type != EXPR_CALL || expr->fn->type != EXPR_SYMBOL)
282 return 0;
283 fn = expr_to_var(expr->fn);
284 if (!fn)
285 return 0;
286 if (strcmp(fn, "kcalloc") == 0)
287 return 1;
288 if (option_project == PROJ_KERNEL && strstr(fn, "zalloc"))
289 return 1;
290 return 0;
293 void __fake_struct_member_assignments(struct expression *expr)
295 struct symbol *struct_type;
296 struct symbol *left_type;
298 if (expr->op != '=')
299 return;
301 if (is_zero(expr->right))
302 return;
304 left_type = get_type(expr->left);
305 if (left_type &&
306 left_type->type != SYM_PTR &&
307 left_type->type != SYM_STRUCT &&
308 left_type != &ulong_ctype)
309 return;
311 struct_type = get_struct_type(expr->left);
312 if (!struct_type)
313 return;
315 if (returns_zeroed_mem(expr->right))
316 __struct_members_copy(COPY_MEMSET, expr, expr->left, zero_expr());
317 else
318 __struct_members_copy(COPY_NORMAL, expr, expr->left, expr->right);
321 static void match_memset(const char *fn, struct expression *expr, void *_size_arg)
323 struct expression *buf;
324 struct expression *val;
326 buf = get_argument_from_call_expr(expr->args, 0);
327 val = get_argument_from_call_expr(expr->args, 1);
329 buf = strip_expr(buf);
330 __struct_members_copy(COPY_MEMSET, expr, remove_addr(buf), val);
333 static void match_memcpy(const char *fn, struct expression *expr, void *_arg)
335 struct expression *dest;
336 struct expression *src;
338 dest = get_argument_from_call_expr(expr->args, 0);
339 src = get_argument_from_call_expr(expr->args, 1);
341 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), remove_addr(src));
344 static void match_memcpy_unknown(const char *fn, struct expression *expr, void *_arg)
346 struct expression *dest;
348 dest = get_argument_from_call_expr(expr->args, 0);
349 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), NULL);
352 static void match_sscanf(const char *fn, struct expression *expr, void *unused)
354 struct expression *arg;
355 int i;
357 i = -1;
358 FOR_EACH_PTR(expr->args, arg) {
359 if (++i < 2)
360 continue;
361 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(arg), NULL);
362 } END_FOR_EACH_PTR(arg);
365 static void unop_expr(struct expression *expr)
367 if (expr->op != SPECIAL_INCREMENT &&
368 expr->op != SPECIAL_DECREMENT)
369 return;
371 if (!is_pointer(expr))
372 return;
373 __struct_members_copy(COPY_MEMCPY, expr, expr->unop, NULL);
376 static void register_clears_param(void)
378 struct token *token;
379 char name[256];
380 const char *function;
381 int param;
383 if (option_project == PROJ_NONE)
384 return;
386 snprintf(name, 256, "%s.clears_argument", option_project_str);
388 token = get_tokens_file(name);
389 if (!token)
390 return;
391 if (token_type(token) != TOKEN_STREAMBEGIN)
392 return;
393 token = token->next;
394 while (token_type(token) != TOKEN_STREAMEND) {
395 if (token_type(token) != TOKEN_IDENT)
396 return;
397 function = show_ident(token->ident);
398 token = token->next;
399 if (token_type(token) != TOKEN_NUMBER)
400 return;
401 param = atoi(token->number);
402 add_function_hook(function, &match_memcpy_unknown, INT_PTR(param));
403 token = token->next;
405 clear_token_alloc();
408 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
410 struct expression *arg;
412 while (expr->type == EXPR_ASSIGNMENT)
413 expr = strip_expr(expr->right);
414 if (expr->type != EXPR_CALL)
415 return;
418 * FIXME: __struct_members_copy() requires an expression but
419 * get_variable_from_key() returns a name/sym pair so that doesn't
420 * work here.
422 if (strcmp(key, "$") != 0)
423 return;
425 arg = get_argument_from_call_expr(expr->args, param);
426 if (!arg)
427 return;
429 if (strcmp(value, "0") == 0)
430 __struct_members_copy(COPY_MEMSET, expr, remove_addr(arg), zero_expr());
431 else
432 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(arg), NULL);
435 void register_struct_assignment(int id)
437 add_function_hook("memset", &match_memset, NULL);
438 add_function_hook("__memset", &match_memset, NULL);
440 add_function_hook("memcpy", &match_memcpy, INT_PTR(0));
441 add_function_hook("memmove", &match_memcpy, INT_PTR(0));
442 add_function_hook("__memcpy", &match_memcpy, INT_PTR(0));
443 add_function_hook("__memmove", &match_memcpy, INT_PTR(0));
445 add_function_hook("sscanf", &match_sscanf, NULL);
447 add_hook(&unop_expr, OP_HOOK);
448 register_clears_param();
449 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);