11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2017 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
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 is for smatch_extra.c to use.  It sort of like check_assigned_expr.c but
201f5207b7SJohn Levon  * more limited.  Say a function returns "64min-s64max[$0->data]" and the caller
211f5207b7SJohn Levon  * does "struct whatever *p = get_data(dev);" then we want to record that p is
221f5207b7SJohn Levon  * now the same as "dev->data".  Then if we update "p->foo" it means we can
231f5207b7SJohn Levon  * update "dev->data->foo" as well.
241f5207b7SJohn Levon  *
251f5207b7SJohn Levon  */
261f5207b7SJohn Levon 
271f5207b7SJohn Levon #include "smatch.h"
281f5207b7SJohn Levon #include "smatch_slist.h"
291f5207b7SJohn Levon #include "smatch_extra.h"
301f5207b7SJohn Levon 
311f5207b7SJohn Levon static int my_id;
321f5207b7SJohn Levon static int link_id;
331f5207b7SJohn Levon 
alloc_my_state(const char * name,struct symbol * sym)341f5207b7SJohn Levon static struct smatch_state *alloc_my_state(const char *name, struct symbol *sym)
351f5207b7SJohn Levon {
361f5207b7SJohn Levon 	struct smatch_state *state;
371f5207b7SJohn Levon 
381f5207b7SJohn Levon 	state = __alloc_smatch_state(0);
391f5207b7SJohn Levon 	state->name = alloc_sname(name);
401f5207b7SJohn Levon 	state->data = sym;
411f5207b7SJohn Levon 	return state;
421f5207b7SJohn Levon }
431f5207b7SJohn Levon 
undef(struct sm_state * sm,struct expression * mod_expr)441f5207b7SJohn Levon static void undef(struct sm_state *sm, struct expression *mod_expr)
451f5207b7SJohn Levon {
461f5207b7SJohn Levon 	if (__in_fake_parameter_assign)
471f5207b7SJohn Levon 		return;
481f5207b7SJohn Levon 	set_state(my_id, sm->name, sm->sym, &undefined);
491f5207b7SJohn Levon }
501f5207b7SJohn Levon 
map_call_to_other_name_sym(const char * name,struct symbol * sym,struct symbol ** new_sym)511f5207b7SJohn Levon char *map_call_to_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
521f5207b7SJohn Levon {
531f5207b7SJohn Levon 	struct smatch_state *state;
541f5207b7SJohn Levon 	int skip;
551f5207b7SJohn Levon 	char buf[256];
561f5207b7SJohn Levon 
571f5207b7SJohn Levon 	/* skip 'foo->'.  This was checked in the caller. */
58*efe51d0cSJohn Levon 	skip = sym->ident->len + 2;
591f5207b7SJohn Levon 
601f5207b7SJohn Levon 	state = get_state(my_id, sym->ident->name, sym);
611f5207b7SJohn Levon 	if (!state || !state->data)
621f5207b7SJohn Levon 		return NULL;
631f5207b7SJohn Levon 
641f5207b7SJohn Levon 	snprintf(buf, sizeof(buf), "%s->%s", state->name, name + skip);
651f5207b7SJohn Levon 	*new_sym = state->data;
661f5207b7SJohn Levon 	return alloc_string(buf);
671f5207b7SJohn Levon }
681f5207b7SJohn Levon 
map_my_state_long_to_short(struct sm_state * sm,const char * name,struct symbol * sym,struct symbol ** new_sym,bool stack)691f5207b7SJohn Levon static char *map_my_state_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
701f5207b7SJohn Levon {
711f5207b7SJohn Levon 	int len;
721f5207b7SJohn Levon 	char buf[256];
731f5207b7SJohn Levon 
741f5207b7SJohn Levon 	if (sm->state->data != sym)
751f5207b7SJohn Levon 		return NULL;
761f5207b7SJohn Levon 	len = strlen(sm->state->name);
771f5207b7SJohn Levon 	if (strncmp(name, sm->state->name, len) != 0)
781f5207b7SJohn Levon 		return NULL;
791f5207b7SJohn Levon 
801f5207b7SJohn Levon 	if (name[len] == '.')
811f5207b7SJohn Levon 		return NULL;
821f5207b7SJohn Levon 	if (!stack && name[len] != '-')
831f5207b7SJohn Levon 		return NULL;
841f5207b7SJohn Levon 	snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len);
851f5207b7SJohn Levon 	*new_sym = sm->sym;
861f5207b7SJohn Levon 	return alloc_string(buf);
871f5207b7SJohn Levon }
881f5207b7SJohn Levon 
891f5207b7SJohn Levon /*
901f5207b7SJohn Levon  * Normally, we expect people to consistently refer to variables by the shortest
911f5207b7SJohn Levon  * name.  So they use "b->a" instead of "foo->bar.a" when both point to the
921f5207b7SJohn Levon  * same memory location.  However, when we're dealing across function boundaries
931f5207b7SJohn Levon  * then sometimes we pass frob(foo) which sets foo->bar.a.  In that case, we
941f5207b7SJohn Levon  * translate it to the shorter name.  Smatch extra updates the shorter name,
951f5207b7SJohn Levon  * which in turn updates the longer name.
961f5207b7SJohn Levon  *
971f5207b7SJohn Levon  */
map_long_to_short_name_sym(const char * name,struct symbol * sym,struct symbol ** new_sym,bool use_stack)98*efe51d0cSJohn Levon char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym, bool use_stack)
991f5207b7SJohn Levon {
1001f5207b7SJohn Levon 	char *ret;
1011f5207b7SJohn Levon 	struct sm_state *sm;
1021f5207b7SJohn Levon 
1031f5207b7SJohn Levon 	*new_sym = NULL;
1041f5207b7SJohn Levon 
1051f5207b7SJohn Levon 	FOR_EACH_SM(__get_cur_stree(), sm) {
1061f5207b7SJohn Levon 		if (sm->owner == my_id) {
107*efe51d0cSJohn Levon 			ret = map_my_state_long_to_short(sm, name, sym, new_sym, use_stack);
108*efe51d0cSJohn Levon 			if (ret) {
109*efe51d0cSJohn Levon 				if (local_debug)
110*efe51d0cSJohn Levon 					sm_msg("%s: my_state: name = '%s' sm = '%s'",
111*efe51d0cSJohn Levon 					       __func__, name, show_sm(sm));
1121f5207b7SJohn Levon 				return ret;
113*efe51d0cSJohn Levon 			}
1141f5207b7SJohn Levon 			continue;
1151f5207b7SJohn Levon 		}
1161f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
1171f5207b7SJohn Levon 
1181f5207b7SJohn Levon 	return NULL;
1191f5207b7SJohn Levon }
1201f5207b7SJohn Levon 
map_call_to_param_name_sym(struct expression * expr,struct symbol ** sym)1211f5207b7SJohn Levon char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym)
1221f5207b7SJohn Levon {
1231f5207b7SJohn Levon 	char *name;
1241f5207b7SJohn Levon 	struct symbol *start_sym;
1251f5207b7SJohn Levon 	struct smatch_state *state;
1261f5207b7SJohn Levon 
1271f5207b7SJohn Levon 	*sym = NULL;
1281f5207b7SJohn Levon 
1291f5207b7SJohn Levon 	name = expr_to_str_sym(expr, &start_sym);
1301f5207b7SJohn Levon 	if (!name)
1311f5207b7SJohn Levon 		return NULL;
1321f5207b7SJohn Levon 	if (expr->type == EXPR_CALL)
1331f5207b7SJohn Levon 		start_sym = expr_to_sym(expr->fn);
1341f5207b7SJohn Levon 
1351f5207b7SJohn Levon 	state = get_state(my_id, name, start_sym);
1361f5207b7SJohn Levon 	free_string(name);
1371f5207b7SJohn Levon 	if (!state || !state->data)
1381f5207b7SJohn Levon 		return NULL;
1391f5207b7SJohn Levon 
1401f5207b7SJohn Levon 	*sym = state->data;
1411f5207b7SJohn Levon 	return alloc_string(state->name);
1421f5207b7SJohn Levon }
1431f5207b7SJohn Levon 
store_mapping_helper(char * left_name,struct symbol * left_sym,struct expression * call,const char * return_string)1441f5207b7SJohn Levon static void store_mapping_helper(char *left_name, struct symbol *left_sym, struct expression *call, const char *return_string)
1451f5207b7SJohn Levon {
1461f5207b7SJohn Levon 	const char *p = return_string;
1471f5207b7SJohn Levon 	char *close;
1481f5207b7SJohn Levon 	int param;
1491f5207b7SJohn Levon 	struct expression *arg, *new;
1501f5207b7SJohn Levon 	char *right_name;
1511f5207b7SJohn Levon 	struct symbol *right_sym;
1521f5207b7SJohn Levon 	char buf[256];
1531f5207b7SJohn Levon 
1541f5207b7SJohn Levon 	while (*p && *p != '[')
1551f5207b7SJohn Levon 		p++;
1561f5207b7SJohn Levon 	if (!*p)
1571f5207b7SJohn Levon 		return;
1581f5207b7SJohn Levon 	p++;
1591f5207b7SJohn Levon 	if (*p != '$')
1601f5207b7SJohn Levon 		return;
1611f5207b7SJohn Levon 
1621f5207b7SJohn Levon 	snprintf(buf, sizeof(buf), "%s", p);
1631f5207b7SJohn Levon 	close = strchr(buf, ']');
1641f5207b7SJohn Levon 	if (!close)
1651f5207b7SJohn Levon 		return;
1661f5207b7SJohn Levon 	*close = '\0';
1671f5207b7SJohn Levon 
1681f5207b7SJohn Levon 	param = atoi(buf + 1);
1691f5207b7SJohn Levon 	arg = get_argument_from_call_expr(call->args, param);
1701f5207b7SJohn Levon 	if (!arg)
1711f5207b7SJohn Levon 		return;
1721f5207b7SJohn Levon 
1731f5207b7SJohn Levon 	new = gen_expression_from_key(arg, buf);
1741f5207b7SJohn Levon 	if (!new)
1751f5207b7SJohn Levon 		return;
1761f5207b7SJohn Levon 
1771f5207b7SJohn Levon 	right_name = expr_to_var_sym(new, &right_sym);
1781f5207b7SJohn Levon 	if (!right_name || !right_sym)
1791f5207b7SJohn Levon 		goto free;
1801f5207b7SJohn Levon 
1811f5207b7SJohn Levon 	set_state(my_id, left_name, left_sym, alloc_my_state(right_name, right_sym));
1821f5207b7SJohn Levon 	store_link(link_id, right_name, right_sym, left_name, left_sym);
1831f5207b7SJohn Levon 
1841f5207b7SJohn Levon free:
1851f5207b7SJohn Levon 	free_string(right_name);
1861f5207b7SJohn Levon }
1871f5207b7SJohn Levon 
__add_return_to_param_mapping(struct expression * expr,const char * return_string)1881f5207b7SJohn Levon void __add_return_to_param_mapping(struct expression *expr, const char *return_string)
1891f5207b7SJohn Levon {
1901f5207b7SJohn Levon 	struct expression *call;
1911f5207b7SJohn Levon 	char *left_name = NULL;
1921f5207b7SJohn Levon 	struct symbol *left_sym;
1931f5207b7SJohn Levon 
1941f5207b7SJohn Levon 	if (expr->type == EXPR_ASSIGNMENT) {
1951f5207b7SJohn Levon 		left_name = expr_to_var_sym(expr->left, &left_sym);
1961f5207b7SJohn Levon 		if (!left_name || !left_sym)
1971f5207b7SJohn Levon 			goto free;
1981f5207b7SJohn Levon 
1991f5207b7SJohn Levon 		call = strip_expr(expr->right);
2001f5207b7SJohn Levon 		if (call->type != EXPR_CALL)
2011f5207b7SJohn Levon 			goto free;
2021f5207b7SJohn Levon 
2031f5207b7SJohn Levon 		store_mapping_helper(left_name, left_sym, call, return_string);
2041f5207b7SJohn Levon 		goto free;
2051f5207b7SJohn Levon 	}
2061f5207b7SJohn Levon 
2071f5207b7SJohn Levon 	if (expr->type == EXPR_CALL &&
2081f5207b7SJohn Levon 	    expr_get_parent_stmt(expr) &&
2091f5207b7SJohn Levon 	    expr_get_parent_stmt(expr)->type == STMT_RETURN) {
2101f5207b7SJohn Levon 		call = strip_expr(expr);
2111f5207b7SJohn Levon 		left_sym = expr_to_sym(call->fn);
2121f5207b7SJohn Levon 		if (!left_sym)
2131f5207b7SJohn Levon 			return;
2141f5207b7SJohn Levon 		left_name = expr_to_str(call);
2151f5207b7SJohn Levon 		if (!left_name)
2161f5207b7SJohn Levon 			return;
2171f5207b7SJohn Levon 
2181f5207b7SJohn Levon 		store_mapping_helper(left_name, left_sym, call, return_string);
2191f5207b7SJohn Levon 		goto free;
2201f5207b7SJohn Levon 
2211f5207b7SJohn Levon 	}
2221f5207b7SJohn Levon 
2231f5207b7SJohn Levon free:
2241f5207b7SJohn Levon 	free_string(left_name);
2251f5207b7SJohn Levon }
2261f5207b7SJohn Levon 
register_return_to_param(int id)2271f5207b7SJohn Levon void register_return_to_param(int id)
2281f5207b7SJohn Levon {
2291f5207b7SJohn Levon 	my_id = id;
230*efe51d0cSJohn Levon 	set_dynamic_states(my_id);
2311f5207b7SJohn Levon 	add_modification_hook(my_id, &undef);
2321f5207b7SJohn Levon }
2331f5207b7SJohn Levon 
register_return_to_param_links(int id)2341f5207b7SJohn Levon void register_return_to_param_links(int id)
2351f5207b7SJohn Levon {
2361f5207b7SJohn Levon 	link_id = id;
2371f5207b7SJohn Levon 	set_up_link_functions(my_id, link_id);
2381f5207b7SJohn Levon }
2391f5207b7SJohn Levon