11f5207b7SJohn Levon /*
21f5207b7SJohn Levon * Copyright (C) 2014 Oracle.
31f5207b7SJohn Levon *
41f5207b7SJohn Levon * This program is free software; you can redistribute it and/or
51f5207b7SJohn Levon * modify it under the terms of the GNU General Public License
61f5207b7SJohn Levon * as published by the Free Software Foundation; either version 2
71f5207b7SJohn Levon * of the License, or (at your option) any later version.
81f5207b7SJohn Levon *
91f5207b7SJohn Levon * This program is distributed in the hope that it will be useful,
101f5207b7SJohn Levon * but WITHOUT ANY WARRANTY; without even the implied warranty of
111f5207b7SJohn Levon * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
121f5207b7SJohn Levon * GNU General Public License for more details.
131f5207b7SJohn Levon *
141f5207b7SJohn Levon * You should have received a copy of the GNU General Public License
151f5207b7SJohn Levon * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
161f5207b7SJohn Levon */
171f5207b7SJohn Levon
181f5207b7SJohn Levon /*
191f5207b7SJohn Levon * This file started out by saying that if you have:
201f5207b7SJohn Levon *
211f5207b7SJohn Levon * struct foo one, two;
221f5207b7SJohn Levon * ...
231f5207b7SJohn Levon * one = two;
241f5207b7SJohn Levon *
251f5207b7SJohn Levon * That's equivalent to saying:
261f5207b7SJohn Levon *
271f5207b7SJohn Levon * one.x = two.x;
281f5207b7SJohn Levon * one.y = two.y;
291f5207b7SJohn Levon *
301f5207b7SJohn Levon * Turning an assignment like that into a bunch of small fake assignments is
311f5207b7SJohn Levon * really useful.
321f5207b7SJohn Levon *
331f5207b7SJohn Levon * The call to memcpy(&one, &two, sizeof(foo)); is the same as "one = two;" so
341f5207b7SJohn Levon * we can re-use the code. And we may as well use it for memset() too.
351f5207b7SJohn Levon * Assigning pointers is almost the same:
361f5207b7SJohn Levon *
371f5207b7SJohn Levon * p1 = p2;
381f5207b7SJohn Levon *
391f5207b7SJohn Levon * Is the same as:
401f5207b7SJohn Levon *
411f5207b7SJohn Levon * p1->x = p2->x;
421f5207b7SJohn Levon * p1->y = p2->y;
431f5207b7SJohn Levon *
441f5207b7SJohn Levon * The problem is that you can go a bit crazy with pointers to pointers.
451f5207b7SJohn Levon *
461f5207b7SJohn Levon * p1->x->y->z->one->two->three = p2->x->y->z->one->two->three;
471f5207b7SJohn Levon *
481f5207b7SJohn Levon * I don't have a proper solution for this problem right now. I just copy one
491f5207b7SJohn Levon * level and don't nest. It should handle limitted nesting but intelligently.
501f5207b7SJohn Levon *
511f5207b7SJohn Levon * The other thing is that you end up with a lot of garbage assignments where
521f5207b7SJohn Levon * we record "x could be anything. x->y could be anything. x->y->z->a->b->c
531f5207b7SJohn Levon * could *also* be anything!". There should be a better way to filter this
541f5207b7SJohn Levon * useless information.
551f5207b7SJohn Levon *
561f5207b7SJohn Levon */
571f5207b7SJohn Levon
581f5207b7SJohn Levon #include "scope.h"
591f5207b7SJohn Levon #include "smatch.h"
601f5207b7SJohn Levon #include "smatch_slist.h"
611f5207b7SJohn Levon #include "smatch_extra.h"
621f5207b7SJohn Levon
631f5207b7SJohn Levon enum {
641f5207b7SJohn Levon COPY_NORMAL,
651f5207b7SJohn Levon COPY_MEMCPY,
661f5207b7SJohn Levon COPY_MEMSET,
671f5207b7SJohn Levon };
681f5207b7SJohn Levon
get_struct_type(struct expression * expr)691f5207b7SJohn Levon static struct symbol *get_struct_type(struct expression *expr)
701f5207b7SJohn Levon {
711f5207b7SJohn Levon struct symbol *type;
721f5207b7SJohn Levon
731f5207b7SJohn Levon type = get_type(expr);
741f5207b7SJohn Levon if (!type)
751f5207b7SJohn Levon return NULL;
761f5207b7SJohn Levon if (type->type == SYM_PTR) {
771f5207b7SJohn Levon type = get_real_base_type(type);
781f5207b7SJohn Levon if (!type)
791f5207b7SJohn Levon return NULL;
801f5207b7SJohn Levon }
811f5207b7SJohn Levon if (type->type == SYM_STRUCT)
821f5207b7SJohn Levon return type;
831f5207b7SJohn Levon if (type->type == SYM_UNION)
841f5207b7SJohn Levon return type;
851f5207b7SJohn Levon return NULL;
861f5207b7SJohn Levon }
871f5207b7SJohn Levon
get_right_base_expr(struct symbol * left_type,struct expression * right)881f5207b7SJohn Levon static struct expression *get_right_base_expr(struct symbol *left_type, struct expression *right)
891f5207b7SJohn Levon {
901f5207b7SJohn Levon struct symbol *struct_type;
911f5207b7SJohn Levon
921f5207b7SJohn Levon if (!right)
931f5207b7SJohn Levon return NULL;
941f5207b7SJohn Levon
951f5207b7SJohn Levon struct_type = get_struct_type(right);
961f5207b7SJohn Levon if (!struct_type)
971f5207b7SJohn Levon return NULL;
981f5207b7SJohn Levon if (struct_type != left_type)
991f5207b7SJohn Levon return NULL;
1001f5207b7SJohn Levon
1011f5207b7SJohn Levon if (right->type == EXPR_PREOP && right->op == '&')
1021f5207b7SJohn Levon right = strip_expr(right->unop);
1031f5207b7SJohn Levon
1041f5207b7SJohn Levon if (right->type == EXPR_CALL)
1051f5207b7SJohn Levon return NULL;
1061f5207b7SJohn Levon
1071f5207b7SJohn Levon if (is_pointer(right))
1081f5207b7SJohn Levon right = deref_expression(right);
1091f5207b7SJohn Levon
1101f5207b7SJohn Levon return right;
1111f5207b7SJohn Levon }
1121f5207b7SJohn Levon
remove_addr(struct expression * expr)1131f5207b7SJohn Levon static struct expression *remove_addr(struct expression *expr)
1141f5207b7SJohn Levon {
1151f5207b7SJohn Levon struct symbol *type;
1161f5207b7SJohn Levon
1171f5207b7SJohn Levon expr = strip_expr(expr);
1181f5207b7SJohn Levon if (!expr)
1191f5207b7SJohn Levon return NULL;
1201f5207b7SJohn Levon
1211f5207b7SJohn Levon if (expr->type == EXPR_PREOP && expr->op == '&')
1221f5207b7SJohn Levon return strip_expr(expr->unop);
1231f5207b7SJohn Levon type = get_type(expr);
1241f5207b7SJohn Levon if (!type)
1251f5207b7SJohn Levon return expr;
1261f5207b7SJohn Levon if (type->type != SYM_PTR && type->type != SYM_ARRAY)
1271f5207b7SJohn Levon return expr;
1281f5207b7SJohn Levon
1291f5207b7SJohn Levon return deref_expression(expr);
1301f5207b7SJohn Levon }
1311f5207b7SJohn Levon
1321f5207b7SJohn Levon static struct expression *faked_expression;
get_faked_expression(void)1331f5207b7SJohn Levon struct expression *get_faked_expression(void)
1341f5207b7SJohn Levon {
1351f5207b7SJohn Levon if (!__in_fake_assign)
1361f5207b7SJohn Levon return NULL;
1371f5207b7SJohn Levon return faked_expression;
1381f5207b7SJohn Levon }
1391f5207b7SJohn Levon
split_fake_expr(struct expression * expr)1401f5207b7SJohn Levon static void split_fake_expr(struct expression *expr)
1411f5207b7SJohn Levon {
1421f5207b7SJohn Levon __in_fake_assign++;
1431f5207b7SJohn Levon __in_fake_struct_assign++;
1441f5207b7SJohn Levon __split_expr(expr);
1451f5207b7SJohn Levon __in_fake_struct_assign--;
1461f5207b7SJohn Levon __in_fake_assign--;
1471f5207b7SJohn Levon }
1481f5207b7SJohn Levon
handle_non_struct_assignments(struct expression * left,struct expression * right)1491f5207b7SJohn Levon static void handle_non_struct_assignments(struct expression *left, struct expression *right)
1501f5207b7SJohn Levon {
1511f5207b7SJohn Levon struct symbol *type;
1521f5207b7SJohn Levon struct expression *assign;
1531f5207b7SJohn Levon
154*6523a3aaSJohn Levon while (right && right->type == EXPR_ASSIGNMENT)
155*6523a3aaSJohn Levon right = strip_parens(right->left);
156*6523a3aaSJohn Levon
1571f5207b7SJohn Levon type = get_type(left);
1581f5207b7SJohn Levon if (!type)
1591f5207b7SJohn Levon return;
1601f5207b7SJohn Levon if (type->type == SYM_PTR) {
1611f5207b7SJohn Levon left = deref_expression(left);
1621f5207b7SJohn Levon if (right)
1631f5207b7SJohn Levon right = deref_expression(right);
1641f5207b7SJohn Levon else
1651f5207b7SJohn Levon right = unknown_value_expression(left);
1661f5207b7SJohn Levon assign = assign_expression(left, '=', right);
1671f5207b7SJohn Levon split_fake_expr(assign);
1681f5207b7SJohn Levon return;
1691f5207b7SJohn Levon }
1701f5207b7SJohn Levon if (type->type != SYM_BASETYPE)
1711f5207b7SJohn Levon return;
1721f5207b7SJohn Levon right = strip_expr(right);
173c85f09ccSJohn Levon type = get_type(right);
174c85f09ccSJohn Levon if (!right || !type || type->type == SYM_ARRAY)
1751f5207b7SJohn Levon right = unknown_value_expression(left);
1761f5207b7SJohn Levon assign = assign_expression(left, '=', right);
1771f5207b7SJohn Levon split_fake_expr(assign);
1781f5207b7SJohn Levon }
1791f5207b7SJohn Levon
set_inner_struct_members(int mode,struct expression * faked,struct expression * left,struct expression * right,struct symbol * member)1801f5207b7SJohn Levon static void set_inner_struct_members(int mode, struct expression *faked, struct expression *left, struct expression *right, struct symbol *member)
1811f5207b7SJohn Levon {
1821f5207b7SJohn Levon struct expression *left_member;
1831f5207b7SJohn Levon struct expression *right_member = NULL; /* silence GCC */
1841f5207b7SJohn Levon struct expression *assign;
1851f5207b7SJohn Levon struct symbol *base = get_real_base_type(member);
1861f5207b7SJohn Levon struct symbol *tmp;
1871f5207b7SJohn Levon
1881f5207b7SJohn Levon if (member->ident) {
1891f5207b7SJohn Levon left = member_expression(left, '.', member->ident);
1901f5207b7SJohn Levon if (mode != COPY_MEMSET && right)
1911f5207b7SJohn Levon right = member_expression(right, '.', member->ident);
1921f5207b7SJohn Levon }
1931f5207b7SJohn Levon
1941f5207b7SJohn Levon FOR_EACH_PTR(base->symbol_list, tmp) {
1951f5207b7SJohn Levon struct symbol *type;
1961f5207b7SJohn Levon
1971f5207b7SJohn Levon type = get_real_base_type(tmp);
1981f5207b7SJohn Levon if (!type)
1991f5207b7SJohn Levon continue;
2001f5207b7SJohn Levon
2011f5207b7SJohn Levon if (type->type == SYM_ARRAY)
2021f5207b7SJohn Levon continue;
2031f5207b7SJohn Levon if (type->type == SYM_UNION || type->type == SYM_STRUCT) {
2041f5207b7SJohn Levon set_inner_struct_members(mode, faked, left, right, tmp);
2051f5207b7SJohn Levon continue;
2061f5207b7SJohn Levon }
2071f5207b7SJohn Levon if (!tmp->ident)
2081f5207b7SJohn Levon continue;
2091f5207b7SJohn Levon
2101f5207b7SJohn Levon left_member = member_expression(left, '.', tmp->ident);
2111f5207b7SJohn Levon
2121f5207b7SJohn Levon switch (mode) {
2131f5207b7SJohn Levon case COPY_NORMAL:
2141f5207b7SJohn Levon case COPY_MEMCPY:
2151f5207b7SJohn Levon if (right)
2161f5207b7SJohn Levon right_member = member_expression(right, '.', tmp->ident);
2171f5207b7SJohn Levon else
2181f5207b7SJohn Levon right_member = unknown_value_expression(left_member);
2191f5207b7SJohn Levon break;
2201f5207b7SJohn Levon case COPY_MEMSET:
2211f5207b7SJohn Levon right_member = right;
2221f5207b7SJohn Levon break;
2231f5207b7SJohn Levon }
2241f5207b7SJohn Levon
2251f5207b7SJohn Levon assign = assign_expression(left_member, '=', right_member);
2261f5207b7SJohn Levon split_fake_expr(assign);
2271f5207b7SJohn Levon } END_FOR_EACH_PTR(tmp);
2281f5207b7SJohn Levon }
2291f5207b7SJohn Levon
__struct_members_copy(int mode,struct expression * faked,struct expression * left,struct expression * right)2301f5207b7SJohn Levon static void __struct_members_copy(int mode, struct expression *faked,
2311f5207b7SJohn Levon struct expression *left,
2321f5207b7SJohn Levon struct expression *right)
2331f5207b7SJohn Levon {
2341f5207b7SJohn Levon struct symbol *struct_type, *tmp, *type;
2351f5207b7SJohn Levon struct expression *left_member;
2361f5207b7SJohn Levon struct expression *right_member;
2371f5207b7SJohn Levon struct expression *assign;
2381f5207b7SJohn Levon int op = '.';
2391f5207b7SJohn Levon
2401f5207b7SJohn Levon if (__in_fake_assign)
2411f5207b7SJohn Levon return;
2421f5207b7SJohn Levon faked_expression = faked;
2431f5207b7SJohn Levon
2441f5207b7SJohn Levon left = strip_expr(left);
2451f5207b7SJohn Levon right = strip_expr(right);
2461f5207b7SJohn Levon
247*6523a3aaSJohn Levon if (left->type == EXPR_PREOP && left->op == '*' && is_pointer(left))
248*6523a3aaSJohn Levon left = preop_expression(left, '(');
249*6523a3aaSJohn Levon
2501f5207b7SJohn Levon struct_type = get_struct_type(left);
2511f5207b7SJohn Levon if (!struct_type) {
2521f5207b7SJohn Levon /*
2531f5207b7SJohn Levon * This is not a struct assignment obviously. But this is where
2541f5207b7SJohn Levon * memcpy() is handled so it feels like a good place to add this
2551f5207b7SJohn Levon * code.
2561f5207b7SJohn Levon */
2571f5207b7SJohn Levon handle_non_struct_assignments(left, right);
2581f5207b7SJohn Levon goto done;
2591f5207b7SJohn Levon }
2601f5207b7SJohn Levon
2611f5207b7SJohn Levon if (is_pointer(left)) {
2621f5207b7SJohn Levon left = deref_expression(left);
2631f5207b7SJohn Levon op = '*';
2641f5207b7SJohn Levon }
2651f5207b7SJohn Levon if (mode != COPY_MEMSET)
2661f5207b7SJohn Levon right = get_right_base_expr(struct_type, right);
2671f5207b7SJohn Levon
2681f5207b7SJohn Levon FOR_EACH_PTR(struct_type->symbol_list, tmp) {
2691f5207b7SJohn Levon type = get_real_base_type(tmp);
2701f5207b7SJohn Levon if (!type)
2711f5207b7SJohn Levon continue;
2721f5207b7SJohn Levon if (type->type == SYM_ARRAY)
2731f5207b7SJohn Levon continue;
2741f5207b7SJohn Levon
2751f5207b7SJohn Levon if (type->type == SYM_UNION || type->type == SYM_STRUCT) {
2761f5207b7SJohn Levon set_inner_struct_members(mode, faked, left, right, tmp);
2771f5207b7SJohn Levon continue;
2781f5207b7SJohn Levon }
2791f5207b7SJohn Levon
2801f5207b7SJohn Levon if (!tmp->ident)
2811f5207b7SJohn Levon continue;
2821f5207b7SJohn Levon
2831f5207b7SJohn Levon left_member = member_expression(left, op, tmp->ident);
2841f5207b7SJohn Levon right_member = NULL;
2851f5207b7SJohn Levon
2861f5207b7SJohn Levon switch (mode) {
2871f5207b7SJohn Levon case COPY_NORMAL:
2881f5207b7SJohn Levon case COPY_MEMCPY:
2891f5207b7SJohn Levon if (right)
2901f5207b7SJohn Levon right_member = member_expression(right, op, tmp->ident);
2911f5207b7SJohn Levon else
2921f5207b7SJohn Levon right_member = unknown_value_expression(left_member);
2931f5207b7SJohn Levon break;
2941f5207b7SJohn Levon case COPY_MEMSET:
2951f5207b7SJohn Levon right_member = right;
2961f5207b7SJohn Levon break;
2971f5207b7SJohn Levon }
2981f5207b7SJohn Levon if (!right_member) {
2991f5207b7SJohn Levon sm_perror("No right member");
3001f5207b7SJohn Levon continue;
3011f5207b7SJohn Levon }
3021f5207b7SJohn Levon assign = assign_expression(left_member, '=', right_member);
3031f5207b7SJohn Levon split_fake_expr(assign);
3041f5207b7SJohn Levon } END_FOR_EACH_PTR(tmp);
3051f5207b7SJohn Levon
3061f5207b7SJohn Levon done:
3071f5207b7SJohn Levon faked_expression = NULL;
3081f5207b7SJohn Levon }
3091f5207b7SJohn Levon
returns_zeroed_mem(struct expression * expr)3101f5207b7SJohn Levon static int returns_zeroed_mem(struct expression *expr)
3111f5207b7SJohn Levon {
3121f5207b7SJohn Levon char *fn;
3131f5207b7SJohn Levon
3141f5207b7SJohn Levon if (expr->type != EXPR_CALL || expr->fn->type != EXPR_SYMBOL)
3151f5207b7SJohn Levon return 0;
3161f5207b7SJohn Levon fn = expr_to_var(expr->fn);
3171f5207b7SJohn Levon if (!fn)
3181f5207b7SJohn Levon return 0;
3191f5207b7SJohn Levon if (strcmp(fn, "kcalloc") == 0)
3201f5207b7SJohn Levon return 1;
3211f5207b7SJohn Levon if (option_project == PROJ_KERNEL && strstr(fn, "zalloc"))
3221f5207b7SJohn Levon return 1;
3231f5207b7SJohn Levon return 0;
3241f5207b7SJohn Levon }
3251f5207b7SJohn Levon
copy_containter_states(struct expression * left,struct expression * right,int offset)3261f5207b7SJohn Levon static int copy_containter_states(struct expression *left, struct expression *right, int offset)
3271f5207b7SJohn Levon {
3281f5207b7SJohn Levon char *left_name = NULL, *right_name = NULL;
3291f5207b7SJohn Levon struct symbol *left_sym, *right_sym;
3301f5207b7SJohn Levon struct sm_state *sm, *new_sm;
3311f5207b7SJohn Levon int ret = 0;
3321f5207b7SJohn Levon int len;
3331f5207b7SJohn Levon char buf[64];
3341f5207b7SJohn Levon char new_name[128];
3351f5207b7SJohn Levon
3361f5207b7SJohn Levon right_name = expr_to_var_sym(right, &right_sym);
3371f5207b7SJohn Levon if (!right_name || !right_sym)
3381f5207b7SJohn Levon goto free;
3391f5207b7SJohn Levon left_name = expr_to_var_sym(left, &left_sym);
3401f5207b7SJohn Levon if (!left_name || !left_sym)
3411f5207b7SJohn Levon goto free;
3421f5207b7SJohn Levon
3431f5207b7SJohn Levon len = snprintf(buf, sizeof(buf), "%s(-%d)", right_name, offset);
3441f5207b7SJohn Levon if (len >= sizeof(buf))
3451f5207b7SJohn Levon goto free;
3461f5207b7SJohn Levon
3471f5207b7SJohn Levon FOR_EACH_SM(__get_cur_stree(), sm) {
3481f5207b7SJohn Levon if (sm->sym != right_sym)
3491f5207b7SJohn Levon continue;
3501f5207b7SJohn Levon if (strncmp(sm->name, buf, len) != 0)
3511f5207b7SJohn Levon continue;
3521f5207b7SJohn Levon snprintf(new_name, sizeof(new_name), "%s%s", left_name, sm->name + len);
3531f5207b7SJohn Levon new_sm = clone_sm(sm);
3541f5207b7SJohn Levon new_sm->name = alloc_sname(new_name);
3551f5207b7SJohn Levon new_sm->sym = left_sym;
3561f5207b7SJohn Levon __set_sm(new_sm);
3571f5207b7SJohn Levon ret = 1;
3581f5207b7SJohn Levon } END_FOR_EACH_SM(sm);
3591f5207b7SJohn Levon free:
3601f5207b7SJohn Levon free_string(left_name);
3611f5207b7SJohn Levon free_string(right_name);
3621f5207b7SJohn Levon return ret;
3631f5207b7SJohn Levon }
3641f5207b7SJohn Levon
handle_param_offsets(struct expression * expr)3651f5207b7SJohn Levon static int handle_param_offsets(struct expression *expr)
3661f5207b7SJohn Levon {
3671f5207b7SJohn Levon struct expression *right;
3681f5207b7SJohn Levon sval_t sval;
3691f5207b7SJohn Levon
3701f5207b7SJohn Levon right = strip_expr(expr->right);
3711f5207b7SJohn Levon
3721f5207b7SJohn Levon if (right->type != EXPR_BINOP || right->op != '-')
3731f5207b7SJohn Levon return 0;
3741f5207b7SJohn Levon
3751f5207b7SJohn Levon if (!get_value(right->right, &sval))
3761f5207b7SJohn Levon return 0;
3771f5207b7SJohn Levon
3781f5207b7SJohn Levon right = get_assigned_expr(right->left);
3791f5207b7SJohn Levon if (!right)
3801f5207b7SJohn Levon return 0;
3811f5207b7SJohn Levon return copy_containter_states(expr->left, right, sval.value);
3821f5207b7SJohn Levon }
3831f5207b7SJohn Levon
returns_container_of(struct expression * expr,int param,char * key,char * value)3841f5207b7SJohn Levon static void returns_container_of(struct expression *expr, int param, char *key, char *value)
3851f5207b7SJohn Levon {
3861f5207b7SJohn Levon struct expression *call, *arg;
3871f5207b7SJohn Levon int offset;
3881f5207b7SJohn Levon
3891f5207b7SJohn Levon if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
3901f5207b7SJohn Levon return;
3911f5207b7SJohn Levon call = strip_expr(expr->right);
3921f5207b7SJohn Levon if (call->type != EXPR_CALL)
3931f5207b7SJohn Levon return;
3941f5207b7SJohn Levon if (param != -1)
3951f5207b7SJohn Levon return;
3961f5207b7SJohn Levon param = atoi(key);
3971f5207b7SJohn Levon offset = atoi(value);
3981f5207b7SJohn Levon
3991f5207b7SJohn Levon arg = get_argument_from_call_expr(call->args, param);
4001f5207b7SJohn Levon if (!arg)
4011f5207b7SJohn Levon return;
4021f5207b7SJohn Levon
4031f5207b7SJohn Levon copy_containter_states(expr->left, arg, -offset);
4041f5207b7SJohn Levon }
4051f5207b7SJohn Levon
__fake_struct_member_assignments(struct expression * expr)4061f5207b7SJohn Levon void __fake_struct_member_assignments(struct expression *expr)
4071f5207b7SJohn Levon {
4081f5207b7SJohn Levon struct symbol *left_type;
4091f5207b7SJohn Levon
4101f5207b7SJohn Levon if (expr->op != '=')
4111f5207b7SJohn Levon return;
4121f5207b7SJohn Levon
413c85f09ccSJohn Levon if (expr_is_zero(expr->right))
4141f5207b7SJohn Levon return;
4151f5207b7SJohn Levon
4161f5207b7SJohn Levon left_type = get_type(expr->left);
4171f5207b7SJohn Levon if (!left_type ||
4181f5207b7SJohn Levon (left_type->type != SYM_PTR &&
4191f5207b7SJohn Levon left_type->type != SYM_STRUCT))
4201f5207b7SJohn Levon return;
4211f5207b7SJohn Levon
4221f5207b7SJohn Levon if (handle_param_offsets(expr))
4231f5207b7SJohn Levon return;
4241f5207b7SJohn Levon
4251f5207b7SJohn Levon if (returns_zeroed_mem(expr->right))
4261f5207b7SJohn Levon __struct_members_copy(COPY_MEMSET, expr, expr->left, zero_expr());
4271f5207b7SJohn Levon else
4281f5207b7SJohn Levon __struct_members_copy(COPY_NORMAL, expr, expr->left, expr->right);
4291f5207b7SJohn Levon }
4301f5207b7SJohn Levon
match_memset(const char * fn,struct expression * expr,void * _size_arg)4311f5207b7SJohn Levon static void match_memset(const char *fn, struct expression *expr, void *_size_arg)
4321f5207b7SJohn Levon {
4331f5207b7SJohn Levon struct expression *buf;
4341f5207b7SJohn Levon struct expression *val;
4351f5207b7SJohn Levon
4361f5207b7SJohn Levon buf = get_argument_from_call_expr(expr->args, 0);
4371f5207b7SJohn Levon val = get_argument_from_call_expr(expr->args, 1);
4381f5207b7SJohn Levon
4391f5207b7SJohn Levon buf = strip_expr(buf);
4401f5207b7SJohn Levon __struct_members_copy(COPY_MEMSET, expr, remove_addr(buf), val);
4411f5207b7SJohn Levon }
4421f5207b7SJohn Levon
match_memcpy(const char * fn,struct expression * expr,void * _arg)4431f5207b7SJohn Levon static void match_memcpy(const char *fn, struct expression *expr, void *_arg)
4441f5207b7SJohn Levon {
4451f5207b7SJohn Levon struct expression *dest;
4461f5207b7SJohn Levon struct expression *src;
4471f5207b7SJohn Levon
4481f5207b7SJohn Levon dest = get_argument_from_call_expr(expr->args, 0);
4491f5207b7SJohn Levon src = get_argument_from_call_expr(expr->args, 1);
4501f5207b7SJohn Levon
4511f5207b7SJohn Levon __struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), remove_addr(src));
4521f5207b7SJohn Levon }
4531f5207b7SJohn Levon
match_memdup(const char * fn,struct expression * call_expr,struct expression * expr,void * _unused)454efe51d0cSJohn Levon static void match_memdup(const char *fn, struct expression *call_expr,
455efe51d0cSJohn Levon struct expression *expr, void *_unused)
456efe51d0cSJohn Levon {
457efe51d0cSJohn Levon struct expression *left, *right, *arg;
458efe51d0cSJohn Levon
459efe51d0cSJohn Levon if (!expr || expr->type != EXPR_ASSIGNMENT)
460efe51d0cSJohn Levon return;
461efe51d0cSJohn Levon
462efe51d0cSJohn Levon left = strip_expr(expr->left);
463efe51d0cSJohn Levon right = strip_expr(expr->right);
464efe51d0cSJohn Levon
465efe51d0cSJohn Levon if (right->type != EXPR_CALL)
466efe51d0cSJohn Levon return;
467efe51d0cSJohn Levon arg = get_argument_from_call_expr(right->args, 0);
468efe51d0cSJohn Levon __struct_members_copy(COPY_MEMCPY, expr, left, arg);
469efe51d0cSJohn Levon }
470efe51d0cSJohn Levon
match_memcpy_unknown(const char * fn,struct expression * expr,void * _arg)4711f5207b7SJohn Levon static void match_memcpy_unknown(const char *fn, struct expression *expr, void *_arg)
4721f5207b7SJohn Levon {
4731f5207b7SJohn Levon struct expression *dest;
4741f5207b7SJohn Levon
4751f5207b7SJohn Levon dest = get_argument_from_call_expr(expr->args, 0);
4761f5207b7SJohn Levon __struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), NULL);
4771f5207b7SJohn Levon }
4781f5207b7SJohn Levon
match_sscanf(const char * fn,struct expression * expr,void * unused)4791f5207b7SJohn Levon static void match_sscanf(const char *fn, struct expression *expr, void *unused)
4801f5207b7SJohn Levon {
4811f5207b7SJohn Levon struct expression *arg;
4821f5207b7SJohn Levon int i;
4831f5207b7SJohn Levon
4841f5207b7SJohn Levon i = -1;
4851f5207b7SJohn Levon FOR_EACH_PTR(expr->args, arg) {
4861f5207b7SJohn Levon if (++i < 2)
4871f5207b7SJohn Levon continue;
4881f5207b7SJohn Levon __struct_members_copy(COPY_MEMCPY, expr, remove_addr(arg), NULL);
4891f5207b7SJohn Levon } END_FOR_EACH_PTR(arg);
4901f5207b7SJohn Levon }
4911f5207b7SJohn Levon
unop_expr(struct expression * expr)4921f5207b7SJohn Levon static void unop_expr(struct expression *expr)
4931f5207b7SJohn Levon {
4941f5207b7SJohn Levon if (expr->op != SPECIAL_INCREMENT &&
4951f5207b7SJohn Levon expr->op != SPECIAL_DECREMENT)
4961f5207b7SJohn Levon return;
4971f5207b7SJohn Levon
4981f5207b7SJohn Levon if (!is_pointer(expr))
4991f5207b7SJohn Levon return;
5001f5207b7SJohn Levon faked_expression = expr;
5011f5207b7SJohn Levon __struct_members_copy(COPY_MEMCPY, expr, expr->unop, NULL);
5021f5207b7SJohn Levon faked_expression = NULL;
5031f5207b7SJohn Levon }
5041f5207b7SJohn Levon
register_clears_param(void)5051f5207b7SJohn Levon static void register_clears_param(void)
5061f5207b7SJohn Levon {
5071f5207b7SJohn Levon struct token *token;
5081f5207b7SJohn Levon char name[256];
5091f5207b7SJohn Levon const char *function;
5101f5207b7SJohn Levon int param;
5111f5207b7SJohn Levon
5121f5207b7SJohn Levon if (option_project == PROJ_NONE)
5131f5207b7SJohn Levon return;
5141f5207b7SJohn Levon
5151f5207b7SJohn Levon snprintf(name, 256, "%s.clears_argument", option_project_str);
5161f5207b7SJohn Levon
5171f5207b7SJohn Levon token = get_tokens_file(name);
5181f5207b7SJohn Levon if (!token)
5191f5207b7SJohn Levon return;
5201f5207b7SJohn Levon if (token_type(token) != TOKEN_STREAMBEGIN)
5211f5207b7SJohn Levon return;
5221f5207b7SJohn Levon token = token->next;
5231f5207b7SJohn Levon while (token_type(token) != TOKEN_STREAMEND) {
5241f5207b7SJohn Levon if (token_type(token) != TOKEN_IDENT)
5251f5207b7SJohn Levon return;
5261f5207b7SJohn Levon function = show_ident(token->ident);
5271f5207b7SJohn Levon token = token->next;
5281f5207b7SJohn Levon if (token_type(token) != TOKEN_NUMBER)
5291f5207b7SJohn Levon return;
5301f5207b7SJohn Levon param = atoi(token->number);
5311f5207b7SJohn Levon add_function_hook(function, &match_memcpy_unknown, INT_PTR(param));
5321f5207b7SJohn Levon token = token->next;
5331f5207b7SJohn Levon }
5341f5207b7SJohn Levon clear_token_alloc();
5351f5207b7SJohn Levon }
5361f5207b7SJohn Levon
db_param_cleared(struct expression * expr,int param,char * key,char * value)5371f5207b7SJohn Levon static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
5381f5207b7SJohn Levon {
5391f5207b7SJohn Levon struct expression *arg;
5401f5207b7SJohn Levon
5411f5207b7SJohn Levon while (expr->type == EXPR_ASSIGNMENT)
5421f5207b7SJohn Levon expr = strip_expr(expr->right);
5431f5207b7SJohn Levon if (expr->type != EXPR_CALL)
5441f5207b7SJohn Levon return;
5451f5207b7SJohn Levon
5461f5207b7SJohn Levon /*
5471f5207b7SJohn Levon * FIXME: __struct_members_copy() requires an expression but
5481f5207b7SJohn Levon * get_variable_from_key() returns a name/sym pair so that doesn't
5491f5207b7SJohn Levon * work here.
5501f5207b7SJohn Levon */
5511f5207b7SJohn Levon if (strcmp(key, "$") != 0)
5521f5207b7SJohn Levon return;
5531f5207b7SJohn Levon
5541f5207b7SJohn Levon arg = get_argument_from_call_expr(expr->args, param);
5551f5207b7SJohn Levon if (!arg)
5561f5207b7SJohn Levon return;
5571f5207b7SJohn Levon
5581f5207b7SJohn Levon if (strcmp(value, "0") == 0)
5591f5207b7SJohn Levon __struct_members_copy(COPY_MEMSET, expr, remove_addr(arg), zero_expr());
5601f5207b7SJohn Levon else
5611f5207b7SJohn Levon __struct_members_copy(COPY_MEMCPY, expr, remove_addr(arg), NULL);
5621f5207b7SJohn Levon }
5631f5207b7SJohn Levon
register_struct_assignment(int id)5641f5207b7SJohn Levon void register_struct_assignment(int id)
5651f5207b7SJohn Levon {
5661f5207b7SJohn Levon add_function_hook("memset", &match_memset, NULL);
5671f5207b7SJohn Levon add_function_hook("__memset", &match_memset, NULL);
5681f5207b7SJohn Levon
5691f5207b7SJohn Levon add_function_hook("memcpy", &match_memcpy, INT_PTR(0));
5701f5207b7SJohn Levon add_function_hook("memmove", &match_memcpy, INT_PTR(0));
5711f5207b7SJohn Levon add_function_hook("__memcpy", &match_memcpy, INT_PTR(0));
5721f5207b7SJohn Levon add_function_hook("__memmove", &match_memcpy, INT_PTR(0));
5731f5207b7SJohn Levon
574efe51d0cSJohn Levon if (option_project == PROJ_KERNEL)
575efe51d0cSJohn Levon return_implies_state_sval("kmemdup", valid_ptr_min_sval, valid_ptr_max_sval, &match_memdup, NULL);
576efe51d0cSJohn Levon
5771f5207b7SJohn Levon add_function_hook("sscanf", &match_sscanf, NULL);
5781f5207b7SJohn Levon
5791f5207b7SJohn Levon add_hook(&unop_expr, OP_HOOK);
5801f5207b7SJohn Levon register_clears_param();
5811f5207b7SJohn Levon select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
5821f5207b7SJohn Levon
5831f5207b7SJohn Levon select_return_states_hook(CONTAINER, &returns_container_of);
5841f5207b7SJohn Levon }
585