11f5207b7SJohn Levon /*
21f5207b7SJohn Levon  * Copyright (C) 2009 Dan Carpenter.
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  * There are a number of ways that variables are modified:
201f5207b7SJohn Levon  * 1) assignment
211f5207b7SJohn Levon  * 2) increment/decrement
221f5207b7SJohn Levon  * 3) assembly
231f5207b7SJohn Levon  * 4) inside functions.
241f5207b7SJohn Levon  *
251f5207b7SJohn Levon  * For setting stuff inside a function then, of course, it's more accurate if
261f5207b7SJohn Levon  * you have the cross function database built.  Otherwise we are super
271f5207b7SJohn Levon  * aggressive about marking things as modified and if you have "frob(foo);" then
281f5207b7SJohn Levon  * we assume "foo->bar" is modified.
291f5207b7SJohn Levon  */
301f5207b7SJohn Levon 
311f5207b7SJohn Levon #include <stdlib.h>
321f5207b7SJohn Levon #include <stdio.h>
331f5207b7SJohn Levon #include "smatch.h"
341f5207b7SJohn Levon #include "smatch_extra.h"
351f5207b7SJohn Levon #include "smatch_slist.h"
361f5207b7SJohn Levon 
371f5207b7SJohn Levon enum {
381f5207b7SJohn Levon 	EARLY = 0,
391f5207b7SJohn Levon 	LATE = 1,
401f5207b7SJohn Levon 	BOTH = 2
411f5207b7SJohn Levon };
421f5207b7SJohn Levon 
431f5207b7SJohn Levon static modification_hook **hooks;
441f5207b7SJohn Levon static modification_hook **hooks_late;
451f5207b7SJohn Levon 
461f5207b7SJohn Levon ALLOCATOR(modification_data, "modification data");
471f5207b7SJohn Levon 
481f5207b7SJohn Levon static int my_id;
alloc_my_state(struct expression * expr,struct smatch_state * prev)491f5207b7SJohn Levon static struct smatch_state *alloc_my_state(struct expression *expr, struct smatch_state *prev)
501f5207b7SJohn Levon {
511f5207b7SJohn Levon 	struct smatch_state *state;
521f5207b7SJohn Levon 	struct modification_data *data;
531f5207b7SJohn Levon 	char *name;
541f5207b7SJohn Levon 
551f5207b7SJohn Levon 	expr = strip_expr(expr);
561f5207b7SJohn Levon 	name = expr_to_str(expr);
57efe51d0cSJohn Levon 	if (!name)
58efe51d0cSJohn Levon 		return NULL;
59efe51d0cSJohn Levon 
60efe51d0cSJohn Levon 	state = __alloc_smatch_state(0);
611f5207b7SJohn Levon 	state->name = alloc_sname(name);
621f5207b7SJohn Levon 	free_string(name);
631f5207b7SJohn Levon 
641f5207b7SJohn Levon 	data = __alloc_modification_data(0);
651f5207b7SJohn Levon 	data->prev = prev;
661f5207b7SJohn Levon 	data->cur = expr;
671f5207b7SJohn Levon 	state->data = data;
681f5207b7SJohn Levon 
691f5207b7SJohn Levon 	return state;
701f5207b7SJohn Levon }
711f5207b7SJohn Levon 
add_modification_hook(int owner,modification_hook * call_back)721f5207b7SJohn Levon void add_modification_hook(int owner, modification_hook *call_back)
731f5207b7SJohn Levon {
741f5207b7SJohn Levon 	if (hooks[owner])
751f5207b7SJohn Levon 		sm_fatal("multiple modification hooks for %s", check_name(owner));
761f5207b7SJohn Levon 	hooks[owner] = call_back;
771f5207b7SJohn Levon }
781f5207b7SJohn Levon 
add_modification_hook_late(int owner,modification_hook * call_back)791f5207b7SJohn Levon void add_modification_hook_late(int owner, modification_hook *call_back)
801f5207b7SJohn Levon {
811f5207b7SJohn Levon 	if (hooks_late[owner])
821f5207b7SJohn Levon 		sm_fatal("multiple late modification hooks for %s", check_name(owner));
831f5207b7SJohn Levon 	hooks_late[owner] = call_back;
841f5207b7SJohn Levon }
851f5207b7SJohn Levon 
matches(char * name,struct symbol * sym,struct sm_state * sm)861f5207b7SJohn Levon static int matches(char *name, struct symbol *sym, struct sm_state *sm)
871f5207b7SJohn Levon {
881f5207b7SJohn Levon 	int len;
891f5207b7SJohn Levon 
901f5207b7SJohn Levon 	if (sym != sm->sym)
911f5207b7SJohn Levon 		return false;
921f5207b7SJohn Levon 
931f5207b7SJohn Levon 	len = strlen(name);
941f5207b7SJohn Levon 	if (strncmp(sm->name, name, len) == 0) {
951f5207b7SJohn Levon 		if (sm->name[len] == '\0')
961f5207b7SJohn Levon 			return true;
971f5207b7SJohn Levon 		if (sm->name[len] == '-' || sm->name[len] == '.')
981f5207b7SJohn Levon 			return true;
991f5207b7SJohn Levon 	}
1001f5207b7SJohn Levon 	if (sm->name[0] != '*')
1011f5207b7SJohn Levon 		return false;
1021f5207b7SJohn Levon 	if (strncmp(sm->name + 1, name, len) == 0) {
1031f5207b7SJohn Levon 		if (sm->name[len + 1] == '\0')
1041f5207b7SJohn Levon 			return true;
1051f5207b7SJohn Levon 		if (sm->name[len + 1] == '-' || sm->name[len + 1] == '.')
1061f5207b7SJohn Levon 			return true;
1071f5207b7SJohn Levon 	}
1081f5207b7SJohn Levon 	return false;
1091f5207b7SJohn Levon }
1101f5207b7SJohn Levon 
call_modification_hooks_name_sym(char * name,struct symbol * sym,struct expression * mod_expr,int late)1111f5207b7SJohn Levon static void call_modification_hooks_name_sym(char *name, struct symbol *sym, struct expression *mod_expr, int late)
1121f5207b7SJohn Levon {
1131f5207b7SJohn Levon 	struct sm_state *sm;
1141f5207b7SJohn Levon 	struct smatch_state *prev;
1151f5207b7SJohn Levon 	int match;
1161f5207b7SJohn Levon 
1171f5207b7SJohn Levon 	prev = get_state(my_id, name, sym);
1181f5207b7SJohn Levon 
1191f5207b7SJohn Levon 	if (cur_func_sym && !__in_fake_assign)
1201f5207b7SJohn Levon 		set_state(my_id, name, sym, alloc_my_state(mod_expr, prev));
1211f5207b7SJohn Levon 
1221f5207b7SJohn Levon 	FOR_EACH_SM(__get_cur_stree(), sm) {
1231f5207b7SJohn Levon 		if (sm->owner > num_checks)
1241f5207b7SJohn Levon 			continue;
1251f5207b7SJohn Levon 		match = matches(name, sym, sm);
1261f5207b7SJohn Levon 		if (!match)
1271f5207b7SJohn Levon 			continue;
1281f5207b7SJohn Levon 
1291f5207b7SJohn Levon 		if (late == EARLY || late == BOTH) {
1301f5207b7SJohn Levon 			if (hooks[sm->owner])
1311f5207b7SJohn Levon 				(hooks[sm->owner])(sm, mod_expr);
1321f5207b7SJohn Levon 		}
1331f5207b7SJohn Levon 		if (late == LATE || late == BOTH) {
1341f5207b7SJohn Levon 			if (hooks_late[sm->owner])
1351f5207b7SJohn Levon 				(hooks_late[sm->owner])(sm, mod_expr);
1361f5207b7SJohn Levon 		}
1371f5207b7SJohn Levon 
1381f5207b7SJohn Levon 	} END_FOR_EACH_SM(sm);
1391f5207b7SJohn Levon }
1401f5207b7SJohn Levon 
call_modification_hooks(struct expression * expr,struct expression * mod_expr,int late)1411f5207b7SJohn Levon static void call_modification_hooks(struct expression *expr, struct expression *mod_expr, int late)
1421f5207b7SJohn Levon {
1431f5207b7SJohn Levon 	char *name;
1441f5207b7SJohn Levon 	struct symbol *sym;
1451f5207b7SJohn Levon 
1461f5207b7SJohn Levon 	name = expr_to_known_chunk_sym(expr, &sym);
1471f5207b7SJohn Levon 	if (!name)
1481f5207b7SJohn Levon 		goto free;
1491f5207b7SJohn Levon 	call_modification_hooks_name_sym(name, sym, mod_expr, late);
1501f5207b7SJohn Levon free:
1511f5207b7SJohn Levon 	free_string(name);
1521f5207b7SJohn Levon }
1531f5207b7SJohn Levon 
db_param_add(struct expression * expr,int param,char * key,char * value)1541f5207b7SJohn Levon static void db_param_add(struct expression *expr, int param, char *key, char *value)
1551f5207b7SJohn Levon {
156*c85f09ccSJohn Levon 	struct expression *arg;
1571f5207b7SJohn Levon 	char *name, *other_name;
1581f5207b7SJohn Levon 	struct symbol *sym, *other_sym;
1591f5207b7SJohn Levon 
1601f5207b7SJohn Levon 	while (expr->type == EXPR_ASSIGNMENT)
1611f5207b7SJohn Levon 		expr = strip_expr(expr->right);
1621f5207b7SJohn Levon 	if (expr->type != EXPR_CALL)
1631f5207b7SJohn Levon 		return;
1641f5207b7SJohn Levon 
1651f5207b7SJohn Levon 	arg = get_argument_from_call_expr(expr->args, param);
1661f5207b7SJohn Levon 	if (!arg)
1671f5207b7SJohn Levon 		return;
1681f5207b7SJohn Levon 
1691f5207b7SJohn Levon 	name = get_variable_from_key(arg, key, &sym);
1701f5207b7SJohn Levon 	if (!name || !sym)
1711f5207b7SJohn Levon 		goto free;
1721f5207b7SJohn Levon 
1731f5207b7SJohn Levon 	__in_fake_assign++;
1741f5207b7SJohn Levon 	call_modification_hooks_name_sym(name, sym, expr, BOTH);
1751f5207b7SJohn Levon 	__in_fake_assign--;
1761f5207b7SJohn Levon 
177efe51d0cSJohn Levon 	other_name = get_other_name_sym(name, sym, &other_sym);
1781f5207b7SJohn Levon 	if (other_name) {
1791f5207b7SJohn Levon 		__in_fake_assign++;
1801f5207b7SJohn Levon 		call_modification_hooks_name_sym(other_name, other_sym, expr, BOTH);
1811f5207b7SJohn Levon 		__in_fake_assign--;
1821f5207b7SJohn Levon 		free_string(other_name);
1831f5207b7SJohn Levon 	}
1841f5207b7SJohn Levon 
1851f5207b7SJohn Levon free:
1861f5207b7SJohn Levon 	free_string(name);
1871f5207b7SJohn Levon }
1881f5207b7SJohn Levon 
match_assign(struct expression * expr,int late)1891f5207b7SJohn Levon static void match_assign(struct expression *expr, int late)
1901f5207b7SJohn Levon {
1911f5207b7SJohn Levon 	call_modification_hooks(expr->left, expr, late);
1921f5207b7SJohn Levon }
1931f5207b7SJohn Levon 
unop_expr(struct expression * expr,int late)1941f5207b7SJohn Levon static void unop_expr(struct expression *expr, int late)
1951f5207b7SJohn Levon {
1961f5207b7SJohn Levon 	if (expr->op != SPECIAL_DECREMENT && expr->op != SPECIAL_INCREMENT)
1971f5207b7SJohn Levon 		return;
1981f5207b7SJohn Levon 
1991f5207b7SJohn Levon 	call_modification_hooks(expr->unop, expr, late);
2001f5207b7SJohn Levon }
2011f5207b7SJohn Levon 
match_call(struct expression * expr)2021f5207b7SJohn Levon static void match_call(struct expression *expr)
2031f5207b7SJohn Levon {
2041f5207b7SJohn Levon 	struct expression *arg, *tmp;
2051f5207b7SJohn Levon 
2061f5207b7SJohn Levon 	/* If we have the DB then trust the DB */
2071f5207b7SJohn Levon 	if (!option_no_db)
2081f5207b7SJohn Levon 		return;
2091f5207b7SJohn Levon 
2101f5207b7SJohn Levon 	FOR_EACH_PTR(expr->args, arg) {
2111f5207b7SJohn Levon 		tmp = strip_expr(arg);
2121f5207b7SJohn Levon 		if (tmp->type == EXPR_PREOP && tmp->op == '&')
2131f5207b7SJohn Levon 			call_modification_hooks(tmp->unop, expr, BOTH);
2141f5207b7SJohn Levon 		else
2151f5207b7SJohn Levon 			call_modification_hooks(deref_expression(tmp), expr, BOTH);
2161f5207b7SJohn Levon 	} END_FOR_EACH_PTR(arg);
2171f5207b7SJohn Levon }
2181f5207b7SJohn Levon 
asm_expr(struct statement * stmt,int late)2191f5207b7SJohn Levon static void asm_expr(struct statement *stmt, int late)
2201f5207b7SJohn Levon {
2211f5207b7SJohn Levon 	struct expression *expr;
2221f5207b7SJohn Levon 
2231f5207b7SJohn Levon 	FOR_EACH_PTR(stmt->asm_outputs, expr) {
224*c85f09ccSJohn Levon 		if (expr->type != EXPR_ASM_OPERAND)
2251f5207b7SJohn Levon 			continue;
226*c85f09ccSJohn Levon 		call_modification_hooks(expr->expr, NULL, late);
2271f5207b7SJohn Levon 	} END_FOR_EACH_PTR(expr);
2281f5207b7SJohn Levon }
2291f5207b7SJohn Levon 
match_assign_early(struct expression * expr)2301f5207b7SJohn Levon static void match_assign_early(struct expression *expr)
2311f5207b7SJohn Levon {
2321f5207b7SJohn Levon 	match_assign(expr, EARLY);
2331f5207b7SJohn Levon }
2341f5207b7SJohn Levon 
unop_expr_early(struct expression * expr)2351f5207b7SJohn Levon static void unop_expr_early(struct expression *expr)
2361f5207b7SJohn Levon {
2371f5207b7SJohn Levon 	unop_expr(expr, EARLY);
2381f5207b7SJohn Levon }
2391f5207b7SJohn Levon 
asm_expr_early(struct statement * stmt)2401f5207b7SJohn Levon static void asm_expr_early(struct statement *stmt)
2411f5207b7SJohn Levon {
2421f5207b7SJohn Levon 	asm_expr(stmt, EARLY);
2431f5207b7SJohn Levon }
2441f5207b7SJohn Levon 
match_assign_late(struct expression * expr)2451f5207b7SJohn Levon static void match_assign_late(struct expression *expr)
2461f5207b7SJohn Levon {
2471f5207b7SJohn Levon 	match_assign(expr, LATE);
2481f5207b7SJohn Levon }
2491f5207b7SJohn Levon 
unop_expr_late(struct expression * expr)2501f5207b7SJohn Levon static void unop_expr_late(struct expression *expr)
2511f5207b7SJohn Levon {
2521f5207b7SJohn Levon 	unop_expr(expr, LATE);
2531f5207b7SJohn Levon }
2541f5207b7SJohn Levon 
asm_expr_late(struct statement * stmt)2551f5207b7SJohn Levon static void asm_expr_late(struct statement *stmt)
2561f5207b7SJohn Levon {
2571f5207b7SJohn Levon 	asm_expr(stmt, LATE);
2581f5207b7SJohn Levon }
2591f5207b7SJohn Levon 
get_modification_state(struct expression * expr)2601f5207b7SJohn Levon struct smatch_state *get_modification_state(struct expression *expr)
2611f5207b7SJohn Levon {
2621f5207b7SJohn Levon 	return get_state_expr(my_id, expr);
2631f5207b7SJohn Levon }
2641f5207b7SJohn Levon 
register_modification_hooks(int id)2651f5207b7SJohn Levon void register_modification_hooks(int id)
2661f5207b7SJohn Levon {
2671f5207b7SJohn Levon 	my_id = id;
2681f5207b7SJohn Levon 
269efe51d0cSJohn Levon 	set_dynamic_states(my_id);
270efe51d0cSJohn Levon 
2711f5207b7SJohn Levon 	hooks = malloc((num_checks + 1) * sizeof(*hooks));
2721f5207b7SJohn Levon 	memset(hooks, 0, (num_checks + 1) * sizeof(*hooks));
2731f5207b7SJohn Levon 	hooks_late = malloc((num_checks + 1) * sizeof(*hooks));
2741f5207b7SJohn Levon 	memset(hooks_late, 0, (num_checks + 1) * sizeof(*hooks));
2751f5207b7SJohn Levon 
2761f5207b7SJohn Levon 	add_hook(&match_assign_early, ASSIGNMENT_HOOK);
2771f5207b7SJohn Levon 	add_hook(&unop_expr_early, OP_HOOK);
2781f5207b7SJohn Levon 	add_hook(&asm_expr_early, ASM_HOOK);
2791f5207b7SJohn Levon }
2801f5207b7SJohn Levon 
register_modification_hooks_late(int id)2811f5207b7SJohn Levon void register_modification_hooks_late(int id)
2821f5207b7SJohn Levon {
2831f5207b7SJohn Levon 	add_hook(&match_call, FUNCTION_CALL_HOOK);
2841f5207b7SJohn Levon 
2851f5207b7SJohn Levon 	select_return_states_hook(PARAM_ADD, &db_param_add);
2861f5207b7SJohn Levon 	select_return_states_hook(PARAM_SET, &db_param_add);
2871f5207b7SJohn Levon 
2881f5207b7SJohn Levon 	add_hook(&match_assign_late, ASSIGNMENT_HOOK_AFTER);
2891f5207b7SJohn Levon 	add_hook(&unop_expr_late, OP_HOOK);
2901f5207b7SJohn Levon 	add_hook(&asm_expr_late, ASM_HOOK);
2911f5207b7SJohn Levon }
2921f5207b7SJohn Levon 
293