1 /*
2  * Copyright (C) 2017 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 smatch_extra.c to use.  It sort of like check_assigned_expr.c but
20  * more limited.  Say a function returns "64min-s64max[$0->data]" and the caller
21  * does "struct whatever *p = get_data(dev);" then we want to record that p is
22  * now the same as "dev->data".  Then if we update "p->foo" it means we can
23  * update "dev->data->foo" as well.
24  *
25  */
26 
27 #include "smatch.h"
28 #include "smatch_slist.h"
29 #include "smatch_extra.h"
30 
31 static int my_id;
32 static int link_id;
33 
alloc_my_state(const char * name,struct symbol * sym)34 static struct smatch_state *alloc_my_state(const char *name, struct symbol *sym)
35 {
36 	struct smatch_state *state;
37 
38 	state = __alloc_smatch_state(0);
39 	state->name = alloc_sname(name);
40 	state->data = sym;
41 	return state;
42 }
43 
undef(struct sm_state * sm,struct expression * mod_expr)44 static void undef(struct sm_state *sm, struct expression *mod_expr)
45 {
46 	if (__in_fake_parameter_assign)
47 		return;
48 	set_state(my_id, sm->name, sm->sym, &undefined);
49 }
50 
map_call_to_other_name_sym(const char * name,struct symbol * sym,struct symbol ** new_sym)51 char *map_call_to_other_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym)
52 {
53 	struct smatch_state *state;
54 	int skip;
55 	char buf[256];
56 
57 	/* skip 'foo->'.  This was checked in the caller. */
58 	skip = sym->ident->len + 2;
59 
60 	state = get_state(my_id, sym->ident->name, sym);
61 	if (!state || !state->data)
62 		return NULL;
63 
64 	snprintf(buf, sizeof(buf), "%s->%s", state->name, name + skip);
65 	*new_sym = state->data;
66 	return alloc_string(buf);
67 }
68 
map_my_state_long_to_short(struct sm_state * sm,const char * name,struct symbol * sym,struct symbol ** new_sym,bool stack)69 static char *map_my_state_long_to_short(struct sm_state *sm, const char *name, struct symbol *sym, struct symbol **new_sym, bool stack)
70 {
71 	int len;
72 	char buf[256];
73 
74 	if (sm->state->data != sym)
75 		return NULL;
76 	len = strlen(sm->state->name);
77 	if (strncmp(name, sm->state->name, len) != 0)
78 		return NULL;
79 
80 	if (name[len] == '.')
81 		return NULL;
82 	if (!stack && name[len] != '-')
83 		return NULL;
84 	snprintf(buf, sizeof(buf), "%s%s", sm->name, name + len);
85 	*new_sym = sm->sym;
86 	return alloc_string(buf);
87 }
88 
89 /*
90  * Normally, we expect people to consistently refer to variables by the shortest
91  * name.  So they use "b->a" instead of "foo->bar.a" when both point to the
92  * same memory location.  However, when we're dealing across function boundaries
93  * then sometimes we pass frob(foo) which sets foo->bar.a.  In that case, we
94  * translate it to the shorter name.  Smatch extra updates the shorter name,
95  * which in turn updates the longer name.
96  *
97  */
map_long_to_short_name_sym(const char * name,struct symbol * sym,struct symbol ** new_sym,bool use_stack)98 char *map_long_to_short_name_sym(const char *name, struct symbol *sym, struct symbol **new_sym, bool use_stack)
99 {
100 	char *ret;
101 	struct sm_state *sm;
102 
103 	*new_sym = NULL;
104 
105 	FOR_EACH_SM(__get_cur_stree(), sm) {
106 		if (sm->owner == my_id) {
107 			ret = map_my_state_long_to_short(sm, name, sym, new_sym, use_stack);
108 			if (ret) {
109 				if (local_debug)
110 					sm_msg("%s: my_state: name = '%s' sm = '%s'",
111 					       __func__, name, show_sm(sm));
112 				return ret;
113 			}
114 			continue;
115 		}
116 	} END_FOR_EACH_SM(sm);
117 
118 	return NULL;
119 }
120 
map_call_to_param_name_sym(struct expression * expr,struct symbol ** sym)121 char *map_call_to_param_name_sym(struct expression *expr, struct symbol **sym)
122 {
123 	char *name;
124 	struct symbol *start_sym;
125 	struct smatch_state *state;
126 
127 	*sym = NULL;
128 
129 	name = expr_to_str_sym(expr, &start_sym);
130 	if (!name)
131 		return NULL;
132 	if (expr->type == EXPR_CALL)
133 		start_sym = expr_to_sym(expr->fn);
134 
135 	state = get_state(my_id, name, start_sym);
136 	free_string(name);
137 	if (!state || !state->data)
138 		return NULL;
139 
140 	*sym = state->data;
141 	return alloc_string(state->name);
142 }
143 
store_mapping_helper(char * left_name,struct symbol * left_sym,struct expression * call,const char * return_string)144 static void store_mapping_helper(char *left_name, struct symbol *left_sym, struct expression *call, const char *return_string)
145 {
146 	const char *p = return_string;
147 	char *close;
148 	int param;
149 	struct expression *arg, *new;
150 	char *right_name;
151 	struct symbol *right_sym;
152 	char buf[256];
153 
154 	while (*p && *p != '[')
155 		p++;
156 	if (!*p)
157 		return;
158 	p++;
159 	if (*p != '$')
160 		return;
161 
162 	snprintf(buf, sizeof(buf), "%s", p);
163 	close = strchr(buf, ']');
164 	if (!close)
165 		return;
166 	*close = '\0';
167 
168 	param = atoi(buf + 1);
169 	arg = get_argument_from_call_expr(call->args, param);
170 	if (!arg)
171 		return;
172 
173 	new = gen_expression_from_key(arg, buf);
174 	if (!new)
175 		return;
176 
177 	right_name = expr_to_var_sym(new, &right_sym);
178 	if (!right_name || !right_sym)
179 		goto free;
180 
181 	set_state(my_id, left_name, left_sym, alloc_my_state(right_name, right_sym));
182 	store_link(link_id, right_name, right_sym, left_name, left_sym);
183 
184 free:
185 	free_string(right_name);
186 }
187 
__add_return_to_param_mapping(struct expression * expr,const char * return_string)188 void __add_return_to_param_mapping(struct expression *expr, const char *return_string)
189 {
190 	struct expression *call;
191 	char *left_name = NULL;
192 	struct symbol *left_sym;
193 
194 	if (expr->type == EXPR_ASSIGNMENT) {
195 		left_name = expr_to_var_sym(expr->left, &left_sym);
196 		if (!left_name || !left_sym)
197 			goto free;
198 
199 		call = strip_expr(expr->right);
200 		if (call->type != EXPR_CALL)
201 			goto free;
202 
203 		store_mapping_helper(left_name, left_sym, call, return_string);
204 		goto free;
205 	}
206 
207 	if (expr->type == EXPR_CALL &&
208 	    expr_get_parent_stmt(expr) &&
209 	    expr_get_parent_stmt(expr)->type == STMT_RETURN) {
210 		call = strip_expr(expr);
211 		left_sym = expr_to_sym(call->fn);
212 		if (!left_sym)
213 			return;
214 		left_name = expr_to_str(call);
215 		if (!left_name)
216 			return;
217 
218 		store_mapping_helper(left_name, left_sym, call, return_string);
219 		goto free;
220 
221 	}
222 
223 free:
224 	free_string(left_name);
225 }
226 
register_return_to_param(int id)227 void register_return_to_param(int id)
228 {
229 	my_id = id;
230 	set_dynamic_states(my_id);
231 	add_modification_hook(my_id, &undef);
232 }
233 
register_return_to_param_links(int id)234 void register_return_to_param_links(int id)
235 {
236 	link_id = id;
237 	set_up_link_functions(my_id, link_id);
238 }
239 
240