db: get return states from function pointers if possible
[smatch.git] / smatch_type_val.c
blob7a6049c2a36f5e84d049e664c79bd98410b1be5f
1 /*
2 * Copyright (C) 2013 Oracle.
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.
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.
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
19 * The plan here is to save all the possible values store to a given struct
20 * member.
22 * We will load all the values in to the function_type_val table first then
23 * run a script on that and load all the resulting values into the type_val
24 * table.
26 * So in this file we want to take the union of everything assigned to the
27 * struct member and insert it into the function_type_val at the end.
29 * You would think that we could use smatch_modification_hooks.c or
30 * extra_modification_hook() here to get the information here but in the end we
31 * need to code everything again a third time.
35 #include "smatch.h"
36 #include "smatch_slist.h"
37 #include "smatch_extra.h"
39 static int my_id;
41 struct stree_stack *fn_type_val_stack;
42 struct stree *fn_type_val;
43 struct stree *global_type_val;
45 static char *db_vals;
46 static int get_vals(void *unused, int argc, char **argv, char **azColName)
48 db_vals = alloc_string(argv[0]);
49 return 0;
52 static void match_inline_start(struct expression *expr)
54 push_stree(&fn_type_val_stack, fn_type_val);
55 fn_type_val = NULL;
58 static void match_inline_end(struct expression *expr)
60 free_stree(&fn_type_val);
61 fn_type_val = pop_stree(&fn_type_val_stack);
64 int get_db_type_rl(struct expression *expr, struct range_list **rl)
66 char *member;
67 struct range_list *tmp;
69 member = get_member_name(expr);
70 if (!member)
71 return 0;
73 db_vals = NULL;
74 run_sql(get_vals,
75 "select value from type_value where type = '%s';", member);
76 free_string(member);
77 if (!db_vals)
78 return 0;
79 str_to_rl(&llong_ctype, db_vals, &tmp);
80 tmp = cast_rl(get_type(expr), tmp);
81 if (is_whole_rl(tmp))
82 return 0;
83 *rl = tmp;
84 free_string(db_vals);
86 return 1;
90 * One of the complications is that smatch tries to free a bunch of data at the
91 * end of every function.
93 static struct data_info *clone_dinfo_perm(struct data_info *dinfo)
95 struct data_info *ret;
97 ret = malloc(sizeof(*ret));
98 ret->related = NULL;
99 ret->value_ranges = clone_rl_permanent(dinfo->value_ranges);
100 ret->hard_max = 0;
101 ret->fuzzy_max = dinfo->fuzzy_max;
102 return ret;
105 static struct smatch_state *clone_estate_perm(struct smatch_state *state)
107 struct smatch_state *ret;
109 ret = malloc(sizeof(*ret));
110 ret->name = alloc_string(state->name);
111 ret->data = clone_dinfo_perm(get_dinfo(state));
112 return ret;
115 static void set_state_stree_perm(struct stree **stree, int owner, const char *name,
116 struct symbol *sym, struct smatch_state *state)
118 struct sm_state *sm;
120 sm = malloc(sizeof(*sm));
121 memset(sm, 0, sizeof(*sm));
122 sm->owner = owner;
123 sm->name = name;
124 sm->sym = sym;
125 sm->state = state;
127 overwrite_sm_state_stree(stree, sm);
130 static void add_type_val(char *member, struct range_list *rl)
132 struct smatch_state *old, *add, *new;
134 member = alloc_string(member);
135 old = get_state_stree(fn_type_val, my_id, member, NULL);
136 add = alloc_estate_rl(rl);
137 if (old)
138 new = merge_estates(old, add);
139 else
140 new = add;
141 set_state_stree(&fn_type_val, my_id, member, NULL, new);
144 static void add_global_type_val(char *member, struct range_list *rl)
146 struct smatch_state *old, *add, *new;
148 member = alloc_string(member);
149 old = get_state_stree(global_type_val, my_id, member, NULL);
150 add = alloc_estate_rl(rl);
151 if (old)
152 new = merge_estates(old, add);
153 else
154 new = add;
155 new = clone_estate_perm(new);
156 set_state_stree_perm(&global_type_val, my_id, member, NULL, new);
159 static void match_assign_value(struct expression *expr)
161 char *member, *right_member;
162 struct range_list *rl;
163 struct symbol *type;
165 type = get_type(expr->left);
166 if (type && type->type == SYM_STRUCT)
167 return;
169 member = get_member_name(expr->left);
170 if (!member)
171 return;
173 /* if we're saying foo->mtu = bar->mtu then that doesn't add information */
174 right_member = get_member_name(expr->right);
175 if (right_member && strcmp(right_member, member) == 0)
176 goto free;
178 if (expr->op != '=') {
179 add_type_val(member, alloc_whole_rl(get_type(expr->left)));
180 goto free;
182 get_absolute_rl(expr->right, &rl);
183 rl = cast_rl(type, rl);
184 add_type_val(member, rl);
185 free:
186 free_string(right_member);
187 free_string(member);
191 * If we too: int *p = &my_struct->member then abandon all hope of tracking
192 * my_struct->member.
194 static void match_assign_pointer(struct expression *expr)
196 struct expression *right;
197 char *member;
198 struct range_list *rl;
199 struct symbol *type;
201 right = strip_expr(expr->right);
202 if (right->type != EXPR_PREOP || right->op != '&')
203 return;
204 right = strip_expr(right->unop);
206 member = get_member_name(right);
207 if (!member)
208 return;
209 type = get_type(right);
210 rl = alloc_whole_rl(type);
211 add_type_val(member, rl);
212 free_string(member);
215 static void match_global_assign(struct expression *expr)
217 char *member;
218 struct range_list *rl;
220 member = get_member_name(expr->left);
221 if (!member)
222 return;
223 get_absolute_rl(expr->right, &rl);
224 add_global_type_val(member, rl);
225 free_string(member);
228 static void unop_expr(struct expression *expr)
230 struct range_list *rl;
231 char *member;
233 if (expr->op != SPECIAL_DECREMENT && expr->op != SPECIAL_INCREMENT)
234 return;
236 expr = strip_expr(expr->unop);
237 member = get_member_name(expr);
238 if (!member)
239 return;
240 rl = alloc_whole_rl(get_type(expr));
241 add_type_val(member, rl);
242 free_string(member);
245 static void asm_expr(struct statement *stmt)
247 struct expression *expr;
248 struct range_list *rl;
249 char *member;
250 int state = 0;
252 FOR_EACH_PTR(stmt->asm_outputs, expr) {
253 switch (state) {
254 case 0: /* identifier */
255 case 1: /* constraint */
256 state++;
257 continue;
258 case 2: /* expression */
259 state = 0;
260 member = get_member_name(expr);
261 if (!member)
262 continue;
263 rl = alloc_whole_rl(get_type(expr));
264 add_type_val(member, rl);
265 free_string(member);
266 continue;
268 } END_FOR_EACH_PTR(expr);
271 static void db_param_add(struct expression *expr, int param, char *key, char *value)
273 struct expression *arg;
274 struct symbol *type;
275 struct range_list *rl;
276 char *member;
278 if (strcmp(key, "*$$") != 0)
279 return;
281 while (expr->type == EXPR_ASSIGNMENT)
282 expr = strip_expr(expr->right);
283 if (expr->type != EXPR_CALL)
284 return;
286 arg = get_argument_from_call_expr(expr->args, param);
287 arg = strip_expr(arg);
288 if (!arg)
289 return;
290 type = get_member_type_from_key(arg, key);
291 if (arg->type != EXPR_PREOP || arg->op != '&')
292 return;
293 arg = strip_expr(arg->unop);
295 member = get_member_name(arg);
296 if (!member)
297 return;
298 call_results_to_rl(expr, type, value, &rl);
299 add_type_val(member, rl);
300 free_string(member);
303 static void match_end_func_info(struct symbol *sym)
305 struct sm_state *sm;
307 FOR_EACH_SM(fn_type_val, sm) {
308 sql_insert_function_type_value(sm->name, sm->state->name);
309 } END_FOR_EACH_SM(sm);
311 free_stree(&fn_type_val);
314 static void match_end_file(struct symbol_list *sym_list)
316 struct sm_state *sm;
318 FOR_EACH_SM(global_type_val, sm) {
319 sql_insert_function_type_value(sm->name, sm->state->name);
320 } END_FOR_EACH_SM(sm);
323 void register_type_val(int id)
325 if (!option_info)
326 return;
328 my_id = id;
330 add_hook(&match_assign_value, ASSIGNMENT_HOOK);
331 add_hook(&match_assign_pointer, ASSIGNMENT_HOOK);
332 add_hook(&unop_expr, OP_HOOK);
333 add_hook(&asm_expr, ASM_HOOK);
334 select_return_states_hook(ADDED_VALUE, &db_param_add);
336 add_hook(&match_inline_start, INLINE_FN_START);
337 add_hook(&match_inline_end, INLINE_FN_END);
339 add_hook(&match_end_func_info, END_FUNC_HOOK);
341 add_hook(&match_global_assign, GLOBAL_ASSIGNMENT_HOOK);
342 add_hook(&match_end_file, END_FILE_HOOK);