1/*
2 * Copyright (C) 2012 Oracle.
3 *
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.
8 *
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.
13 *
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
16 */
17
18/*
19 * This is for functions like:
20 *
21 * void foo(int *x)
22 * {
23 * 	if (*x == 42)
24 *		*x = 0;
25 * }
26 *
27 * The final value of *x depends on the input to the function but with *x == 42
28 * filtered out.
29 *
30 */
31
32#include "smatch.h"
33#include "smatch_extra.h"
34#include "smatch_slist.h"
35
36static int my_id;
37
38static struct stree *start_states;
39static struct stree_stack *saved_stack;
40static void save_start_states(struct statement *stmt)
41{
42	start_states = get_all_states_stree(SMATCH_EXTRA);
43}
44
45static void free_start_states(void)
46{
47	free_stree(&start_states);
48}
49
50static void match_save_states(struct expression *expr)
51{
52	push_stree(&saved_stack, start_states);
53	start_states = NULL;
54}
55
56static void match_restore_states(struct expression *expr)
57{
58	free_stree(&start_states);
59	start_states = pop_stree(&saved_stack);
60}
61
62static struct smatch_state *unmatched_state(struct sm_state *sm)
63{
64	struct smatch_state *state;
65
66	if (parent_is_gone_var_sym(sm->name, sm->sym))
67		return alloc_estate_empty();
68
69	state = __get_state(SMATCH_EXTRA, sm->name, sm->sym);
70	if (state)
71		return state;
72	return alloc_estate_whole(estate_type(sm->state));
73}
74
75static void pre_merge_hook(struct sm_state *cur, struct sm_state *other)
76{
77	struct smatch_state *extra;
78	struct range_list *rl;
79
80	if (estate_rl(other->state))
81		return;
82
83	extra = get_state(SMATCH_EXTRA, cur->name, cur->sym);
84	if (!extra)
85		return;
86
87	rl = rl_intersection(estate_rl(extra), estate_rl(cur->state));
88	if (rl_equiv(rl, estate_rl(cur->state)))
89		return;
90	set_state(my_id, cur->name, cur->sym, alloc_estate_rl(clone_rl(rl)));
91}
92
93static void extra_mod_hook(const char *name, struct symbol *sym, struct expression *expr, struct smatch_state *state)
94{
95	int param;
96
97	if (__in_fake_assign)
98		return;
99
100	param = get_param_num_from_sym(sym);
101	if (param < 0)
102		return;
103
104	/* on stack parameters are handled in smatch_param_limit.c */
105	if (sym->ident && strcmp(sym->ident->name, name) == 0)
106		return;
107
108	set_state(my_id, name, sym, alloc_estate_empty());
109}
110
111/*
112 * This relies on the fact that these states are stored so that
113 * foo->bar is before foo->bar->baz.
114 */
115static int parent_set(struct string_list *list, const char *name)
116{
117	char *tmp;
118	int len;
119	int ret;
120
121	FOR_EACH_PTR(list, tmp) {
122		len = strlen(tmp);
123		ret = strncmp(tmp, name, len);
124		if (ret < 0)
125			continue;
126		if (ret > 0)
127			return 0;
128		if (name[len] == '-')
129			return 1;
130	} END_FOR_EACH_PTR(tmp);
131
132	return 0;
133}
134
135static void print_one_mod_param(int return_id, char *return_ranges,
136			int param, struct sm_state *sm, struct string_list **totally_filtered)
137{
138	const char *param_name;
139
140	param_name = get_param_name(sm);
141	if (!param_name)
142		return;
143	if (is_whole_rl(estate_rl(sm->state)))
144		return;
145	if (!estate_rl(sm->state)) {
146		insert_string(totally_filtered, (char *)sm->name);
147		return;
148	}
149
150	if (is_ignored_kernel_data(param_name)) {
151		insert_string(totally_filtered, (char *)sm->name);
152		return;
153	}
154
155	sql_insert_return_states(return_id, return_ranges, PARAM_FILTER, param,
156			param_name, show_rl(estate_rl(sm->state)));
157}
158
159static void print_return_value_param(int return_id, char *return_ranges, struct expression *expr)
160{
161	struct sm_state *tmp;
162	struct sm_state *sm;
163	struct string_list *totally_filtered = NULL;
164	int param;
165
166	FOR_EACH_MY_SM(SMATCH_EXTRA, __get_cur_stree(), tmp) {
167		param = get_param_num_from_sym(tmp->sym);
168		if (param < 0)
169			continue;
170
171		/* on stack parameters are handled in smatch_param_limit.c */
172		if (tmp->sym->ident && strcmp(tmp->sym->ident->name, tmp->name) == 0)
173			continue;
174
175		if (parent_set(totally_filtered, tmp->name))
176			continue;
177
178		sm = get_sm_state(my_id, tmp->name, tmp->sym);
179		if (sm)
180			print_one_mod_param(return_id, return_ranges, param, sm, &totally_filtered);
181	} END_FOR_EACH_SM(tmp);
182
183	free_ptr_list((struct ptr_list **)&totally_filtered);
184}
185
186int param_has_filter_data(struct sm_state *sm)
187{
188	struct smatch_state *state;
189
190	state = get_state(my_id, sm->name, sm->sym);
191	if (!state) {
192		if (get_assigned_expr_name_sym(sm->name, sm->sym))
193			return 0;
194		return 1;
195	}
196	if (estate_rl(state))
197		return 1;
198	return 0;
199}
200
201void register_param_filter(int id)
202{
203	my_id = id;
204
205	set_dynamic_states(my_id);
206	add_hook(&save_start_states, AFTER_DEF_HOOK);
207	add_hook(&free_start_states, AFTER_FUNC_HOOK);
208
209	add_extra_mod_hook(&extra_mod_hook);
210	add_unmatched_state_hook(my_id, &unmatched_state);
211	add_pre_merge_hook(my_id, &pre_merge_hook);
212	add_merge_hook(my_id, &merge_estates);
213
214	add_hook(&match_save_states, INLINE_FN_START);
215	add_hook(&match_restore_states, INLINE_FN_END);
216
217	add_split_return_callback(&print_return_value_param);
218}
219
220