comparison: improve "foo = min(...);" assignment handling
[smatch.git] / smatch_struct_assignment.c
blobd62772a7b3c48eec9284f6b2f2ca8ad45e88c3b2
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);
113 if (!expr)
114 return NULL;
116 if (expr->type == EXPR_PREOP && expr->op == '&')
117 return strip_expr(expr->unop);
118 type = get_type(expr);
119 if (!type)
120 return expr;
121 if (type->type != SYM_PTR && type->type != SYM_ARRAY)
122 return expr;
124 return deref_expression(expr);
127 static struct expression *faked_expression;
128 struct expression *get_faked_expression(void)
130 if (!__in_fake_assign)
131 return NULL;
132 return faked_expression;
135 static void split_fake_expr(struct expression *expr)
137 __in_fake_assign++;
138 __in_fake_struct_assign++;
139 __split_expr(expr);
140 __in_fake_struct_assign--;
141 __in_fake_assign--;
144 static void handle_non_struct_assignments(struct expression *left, struct expression *right)
146 struct symbol *type;
147 struct expression *assign;
149 type = get_type(left);
150 if (!type)
151 return;
152 if (type->type == SYM_PTR) {
153 left = deref_expression(left);
154 if (right)
155 right = deref_expression(right);
156 else
157 right = unknown_value_expression(left);
158 assign = assign_expression(left, '=', right);
159 split_fake_expr(assign);
160 return;
162 if (type->type != SYM_BASETYPE)
163 return;
164 right = strip_expr(right);
165 if (!right)
166 right = unknown_value_expression(left);
167 assign = assign_expression(left, '=', right);
168 split_fake_expr(assign);
171 static void set_inner_struct_members(int mode, struct expression *faked, struct expression *left, struct expression *right, struct symbol *member)
173 struct expression *left_member;
174 struct expression *right_member = NULL; /* silence GCC */
175 struct expression *assign;
176 struct symbol *base = get_real_base_type(member);
177 struct symbol *tmp;
179 if (member->ident) {
180 left = member_expression(left, '.', member->ident);
181 if (mode != COPY_MEMSET && right)
182 right = member_expression(right, '.', member->ident);
185 FOR_EACH_PTR(base->symbol_list, tmp) {
186 struct symbol *type;
188 type = get_real_base_type(tmp);
189 if (!type)
190 continue;
192 if (type->type == SYM_ARRAY)
193 continue;
194 if (type->type == SYM_UNION || type->type == SYM_STRUCT) {
195 set_inner_struct_members(mode, faked, left, right, tmp);
196 continue;
198 if (!tmp->ident)
199 continue;
201 left_member = member_expression(left, '.', tmp->ident);
203 switch (mode) {
204 case COPY_NORMAL:
205 case COPY_MEMCPY:
206 if (right)
207 right_member = member_expression(right, '.', tmp->ident);
208 else
209 right_member = unknown_value_expression(left_member);
210 break;
211 case COPY_MEMSET:
212 right_member = right;
213 break;
216 assign = assign_expression(left_member, '=', right_member);
217 split_fake_expr(assign);
218 } END_FOR_EACH_PTR(tmp);
221 static void __struct_members_copy(int mode, struct expression *faked,
222 struct expression *left,
223 struct expression *right)
225 struct symbol *struct_type, *tmp, *type;
226 struct expression *left_member;
227 struct expression *right_member;
228 struct expression *assign;
229 int op = '.';
232 if (__in_fake_assign)
233 return;
234 faked_expression = faked;
236 left = strip_expr(left);
237 right = strip_expr(right);
239 struct_type = get_struct_type(left);
240 if (!struct_type) {
242 * This is not a struct assignment obviously. But this is where
243 * memcpy() is handled so it feels like a good place to add this
244 * code.
246 handle_non_struct_assignments(left, right);
247 goto done;
250 if (is_pointer(left)) {
251 left = deref_expression(left);
252 op = '*';
254 if (mode != COPY_MEMSET)
255 right = get_right_base_expr(struct_type, right);
257 FOR_EACH_PTR(struct_type->symbol_list, tmp) {
258 type = get_real_base_type(tmp);
259 if (!type)
260 continue;
261 if (type->type == SYM_ARRAY)
262 continue;
264 if (type->type == SYM_UNION || type->type == SYM_STRUCT) {
265 set_inner_struct_members(mode, faked, left, right, tmp);
266 continue;
269 if (!tmp->ident)
270 continue;
272 left_member = member_expression(left, op, tmp->ident);
273 right_member = NULL;
275 switch (mode) {
276 case COPY_NORMAL:
277 case COPY_MEMCPY:
278 if (right)
279 right_member = member_expression(right, op, tmp->ident);
280 else
281 right_member = unknown_value_expression(left_member);
282 break;
283 case COPY_MEMSET:
284 right_member = right;
285 break;
287 if (!right_member) {
288 sm_msg("internal. No right member");
289 continue;
291 assign = assign_expression(left_member, '=', right_member);
292 split_fake_expr(assign);
293 } END_FOR_EACH_PTR(tmp);
295 done:
296 faked_expression = NULL;
299 static int returns_zeroed_mem(struct expression *expr)
301 char *fn;
303 if (expr->type != EXPR_CALL || expr->fn->type != EXPR_SYMBOL)
304 return 0;
305 fn = expr_to_var(expr->fn);
306 if (!fn)
307 return 0;
308 if (strcmp(fn, "kcalloc") == 0)
309 return 1;
310 if (option_project == PROJ_KERNEL && strstr(fn, "zalloc"))
311 return 1;
312 return 0;
315 static int copy_containter_states(struct expression *left, struct expression *right, int offset)
317 char *left_name = NULL, *right_name = NULL;
318 struct symbol *left_sym, *right_sym;
319 struct sm_state *sm, *new_sm;
320 int ret = 0;
321 int len;
322 char buf[64];
323 char new_name[128];
325 right_name = expr_to_var_sym(right, &right_sym);
326 if (!right_name || !right_sym)
327 goto free;
328 left_name = expr_to_var_sym(left, &left_sym);
329 if (!left_name || !left_sym)
330 goto free;
332 len = snprintf(buf, sizeof(buf), "%s(-%d)", right_name, offset);
333 if (len >= sizeof(buf))
334 goto free;
336 FOR_EACH_SM(__get_cur_stree(), sm) {
337 if (sm->sym != right_sym)
338 continue;
339 if (strncmp(sm->name, buf, len) != 0)
340 continue;
341 snprintf(new_name, sizeof(new_name), "%s%s", left_name, sm->name + len);
342 new_sm = clone_sm(sm);
343 new_sm->name = alloc_sname(new_name);
344 new_sm->sym = left_sym;
345 __set_sm(new_sm);
346 ret = 1;
347 } END_FOR_EACH_SM(sm);
348 free:
349 free_string(left_name);
350 free_string(right_name);
351 return ret;
354 static int handle_param_offsets(struct expression *expr)
356 struct expression *right;
357 sval_t sval;
359 right = strip_expr(expr->right);
361 if (right->type != EXPR_BINOP || right->op != '-')
362 return 0;
364 if (!get_value(right->right, &sval))
365 return 0;
367 right = get_assigned_expr(right->left);
368 if (!right)
369 return 0;
370 return copy_containter_states(expr->left, right, sval.value);
373 static void returns_container_of(struct expression *expr, int param, char *key, char *value)
375 struct expression *call, *arg;
376 int offset;
378 if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
379 return;
380 call = strip_expr(expr->right);
381 if (call->type != EXPR_CALL)
382 return;
383 if (param != -1)
384 return;
385 param = atoi(key);
386 offset = atoi(value);
388 arg = get_argument_from_call_expr(call->args, param);
389 if (!arg)
390 return;
392 copy_containter_states(expr->left, arg, -offset);
395 void __fake_struct_member_assignments(struct expression *expr)
397 struct symbol *left_type;
399 if (expr->op != '=')
400 return;
402 if (is_zero(expr->right))
403 return;
405 left_type = get_type(expr->left);
406 if (!left_type ||
407 (left_type->type != SYM_PTR &&
408 left_type->type != SYM_STRUCT))
409 return;
411 if (handle_param_offsets(expr))
412 return;
414 if (returns_zeroed_mem(expr->right))
415 __struct_members_copy(COPY_MEMSET, expr, expr->left, zero_expr());
416 else
417 __struct_members_copy(COPY_NORMAL, expr, expr->left, expr->right);
420 static void match_memset(const char *fn, struct expression *expr, void *_size_arg)
422 struct expression *buf;
423 struct expression *val;
425 buf = get_argument_from_call_expr(expr->args, 0);
426 val = get_argument_from_call_expr(expr->args, 1);
428 buf = strip_expr(buf);
429 __struct_members_copy(COPY_MEMSET, expr, remove_addr(buf), val);
432 static void match_memcpy(const char *fn, struct expression *expr, void *_arg)
434 struct expression *dest;
435 struct expression *src;
437 dest = get_argument_from_call_expr(expr->args, 0);
438 src = get_argument_from_call_expr(expr->args, 1);
440 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), remove_addr(src));
443 static void match_memcpy_unknown(const char *fn, struct expression *expr, void *_arg)
445 struct expression *dest;
447 dest = get_argument_from_call_expr(expr->args, 0);
448 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), NULL);
451 static void match_sscanf(const char *fn, struct expression *expr, void *unused)
453 struct expression *arg;
454 int i;
456 i = -1;
457 FOR_EACH_PTR(expr->args, arg) {
458 if (++i < 2)
459 continue;
460 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(arg), NULL);
461 } END_FOR_EACH_PTR(arg);
464 static void unop_expr(struct expression *expr)
466 if (expr->op != SPECIAL_INCREMENT &&
467 expr->op != SPECIAL_DECREMENT)
468 return;
470 if (!is_pointer(expr))
471 return;
472 __struct_members_copy(COPY_MEMCPY, expr, expr->unop, NULL);
475 static void register_clears_param(void)
477 struct token *token;
478 char name[256];
479 const char *function;
480 int param;
482 if (option_project == PROJ_NONE)
483 return;
485 snprintf(name, 256, "%s.clears_argument", option_project_str);
487 token = get_tokens_file(name);
488 if (!token)
489 return;
490 if (token_type(token) != TOKEN_STREAMBEGIN)
491 return;
492 token = token->next;
493 while (token_type(token) != TOKEN_STREAMEND) {
494 if (token_type(token) != TOKEN_IDENT)
495 return;
496 function = show_ident(token->ident);
497 token = token->next;
498 if (token_type(token) != TOKEN_NUMBER)
499 return;
500 param = atoi(token->number);
501 add_function_hook(function, &match_memcpy_unknown, INT_PTR(param));
502 token = token->next;
504 clear_token_alloc();
507 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
509 struct expression *arg;
511 while (expr->type == EXPR_ASSIGNMENT)
512 expr = strip_expr(expr->right);
513 if (expr->type != EXPR_CALL)
514 return;
517 * FIXME: __struct_members_copy() requires an expression but
518 * get_variable_from_key() returns a name/sym pair so that doesn't
519 * work here.
521 if (strcmp(key, "$") != 0)
522 return;
524 arg = get_argument_from_call_expr(expr->args, param);
525 if (!arg)
526 return;
528 if (strcmp(value, "0") == 0)
529 __struct_members_copy(COPY_MEMSET, expr, remove_addr(arg), zero_expr());
530 else
531 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(arg), NULL);
534 void register_struct_assignment(int id)
536 add_function_hook("memset", &match_memset, NULL);
537 add_function_hook("__memset", &match_memset, NULL);
539 add_function_hook("memcpy", &match_memcpy, INT_PTR(0));
540 add_function_hook("memmove", &match_memcpy, INT_PTR(0));
541 add_function_hook("__memcpy", &match_memcpy, INT_PTR(0));
542 add_function_hook("__memmove", &match_memcpy, INT_PTR(0));
544 add_function_hook("sscanf", &match_sscanf, NULL);
546 add_hook(&unop_expr, OP_HOOK);
547 register_clears_param();
548 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
550 select_return_states_hook(CONTAINER, &returns_container_of);