xref: /illumos-gate/usr/src/tools/smatch/src/smatch_struct_assignment.c (revision 6523a3aa7f325d64841382707603be7a86e68147)
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