flow: don't fake an impossible default
[smatch.git] / smatch_container_of.c
blob077b8249901689110cff8db9b68221da09caff65
1 /*
2 * Copyright (C) 2017 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
18 #include "smatch.h"
19 #include "smatch_slist.h"
20 #include "smatch_extra.h"
22 static int my_id;
23 static int param_id;
25 static struct stree *used_stree;
26 static struct stree_stack *saved_stack;
28 STATE(used);
30 int get_param_from_container_of(struct expression *expr)
32 struct expression *param_expr;
33 struct symbol *type;
34 sval_t sval;
35 int param;
38 type = get_type(expr);
39 if (!type || type->type != SYM_PTR)
40 return -1;
42 expr = strip_expr(expr);
43 if (expr->type != EXPR_BINOP || expr->op != '-')
44 return -1;
46 if (!get_value(expr->right, &sval))
47 return -1;
48 if (sval.value < 0 || sval.value > 4096)
49 return -1;
51 param_expr = get_assigned_expr(expr->left);
52 if (!param_expr)
53 return -1;
54 param = get_param_num(param_expr);
55 if (param < 0)
56 return -1;
58 return param;
61 int get_offset_from_container_of(struct expression *expr)
63 struct expression *param_expr;
64 struct symbol *type;
65 sval_t sval;
67 type = get_type(expr);
68 if (!type || type->type != SYM_PTR)
69 return -1;
71 expr = strip_expr(expr);
72 if (expr->type != EXPR_BINOP || expr->op != '-')
73 return -1;
75 if (!get_value(expr->right, &sval))
76 return -1;
77 if (sval.value < 0 || sval.value > 4096)
78 return -1;
80 param_expr = get_assigned_expr(expr->left);
81 if (!param_expr)
82 return -1;
84 return sval.value;
87 static int get_container_arg(struct symbol *sym)
89 struct expression *__mptr;
90 int param;
92 if (!sym || !sym->ident)
93 return -1;
95 __mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
96 param = get_param_from_container_of(__mptr);
98 return param;
101 static int get_container_offset(struct symbol *sym)
103 struct expression *__mptr;
104 int offset;
106 if (!sym || !sym->ident)
107 return -1;
109 __mptr = get_assigned_expr_name_sym(sym->ident->name, sym);
110 offset = get_offset_from_container_of(__mptr);
112 return offset;
115 static char *get_container_name(struct sm_state *sm, int offset)
117 static char buf[256];
118 const char *name;
120 name = get_param_name(sm);
121 if (!name)
122 return NULL;
124 if (name[0] == '$')
125 snprintf(buf, sizeof(buf), "$(-%d)%s", offset, name + 1);
126 else if (name[0] == '*' || name[1] == '$')
127 snprintf(buf, sizeof(buf), "*$(-%d)%s", offset, name + 2);
128 else
129 return NULL;
131 return buf;
134 static void get_state_hook(int owner, const char *name, struct symbol *sym)
136 int arg;
138 if (!option_info)
139 return;
140 if (__in_fake_assign)
141 return;
143 arg = get_container_arg(sym);
144 if (arg >= 0)
145 set_state_stree(&used_stree, my_id, name, sym, &used);
148 static void set_param_used(struct expression *call, struct expression *arg, char *key, char *unused)
150 struct symbol *sym;
151 char *name;
152 int arg_nr;
154 name = get_variable_from_key(arg, key, &sym);
155 if (!name || !sym)
156 goto free;
158 arg_nr = get_container_arg(sym);
159 if (arg_nr >= 0)
160 set_state(my_id, name, sym, &used);
161 free:
162 free_string(name);
165 static void process_states(void)
167 struct sm_state *tmp;
168 int arg, offset;
169 const char *name;
171 FOR_EACH_SM(used_stree, tmp) {
172 arg = get_container_arg(tmp->sym);
173 offset = get_container_offset(tmp->sym);
174 if (arg < 0 || offset < 0)
175 continue;
176 name = get_container_name(tmp, offset);
177 if (!name)
178 continue;
179 sql_insert_call_implies(CONTAINER, arg, name, "");
180 } END_FOR_EACH_SM(tmp);
182 free_stree(&used_stree);
185 static void match_function_def(struct symbol *sym)
187 free_stree(&used_stree);
190 static void match_save_states(struct expression *expr)
192 push_stree(&saved_stack, used_stree);
193 used_stree = NULL;
196 static void match_restore_states(struct expression *expr)
198 free_stree(&used_stree);
199 used_stree = pop_stree(&saved_stack);
202 static void print_returns_container_of(int return_id, char *return_ranges, struct expression *expr)
204 int offset;
205 int param;
206 char key[64];
207 char value[64];
209 param = get_param_from_container_of(expr);
210 if (param < 0)
211 return;
212 offset = get_offset_from_container_of(expr);
213 if (offset < 0)
214 return;
216 snprintf(key, sizeof(key), "%d", param);
217 snprintf(value, sizeof(value), "-%d", offset);
219 /* no need to add it to call_implies because it's not really param_used */
220 sql_insert_return_states(return_id, return_ranges, CONTAINER, -1,
221 key, value);
224 static void returns_container_of(struct expression *expr, int param, char *key, char *value)
226 struct expression *call, *arg;
227 int offset;
228 char buf[64];
230 if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
231 return;
232 call = strip_expr(expr->right);
233 if (call->type != EXPR_CALL)
234 return;
235 if (param != -1)
236 return;
237 param = atoi(key);
238 offset = atoi(value);
240 arg = get_argument_from_call_expr(call->args, param);
241 if (!arg)
242 return;
243 if (arg->type != EXPR_SYMBOL)
244 return;
245 param = get_param_num(arg);
246 if (param < 0)
247 return;
248 snprintf(buf, sizeof(buf), "$(%d)", offset);
249 sql_insert_call_implies(CONTAINER, param, buf, "");
252 static struct expression *get_outer_struct(struct expression *expr)
254 struct expression *unop;
256 expr = strip_expr(expr);
257 if (expr->type != EXPR_DEREF)
258 return NULL;
259 unop = strip_expr(expr->deref);
260 if (unop->type == EXPR_SYMBOL)
261 return unop;
262 if (unop->type != EXPR_PREOP || unop->op != '*')
263 return NULL;
264 return strip_expr(unop->unop);
267 static void match_call(struct expression *call)
269 struct expression *ptr, *unop, *arg, *arg_unop;
270 struct symbol *type;
271 int i, offset;
272 int arg_offset = 0;
273 char offset_str[12];
274 char param_str[12];
275 bool star = 0;
277 ptr = strip_expr(call->fn);
278 unop = get_outer_struct(ptr);
279 if (!unop)
280 return;
282 i = -1;
283 FOR_EACH_PTR(call->args, arg) {
284 i++;
285 if (expr_equiv(arg, unop))
286 goto found;
287 } END_FOR_EACH_PTR(arg);
289 i = -1;
290 FOR_EACH_PTR(call->args, arg) {
291 i++;
292 arg_unop = get_outer_struct(arg);
293 if (!arg_unop)
294 continue;
295 if (!expr_equiv(arg_unop, unop))
296 continue;
297 arg_offset = get_member_offset_from_deref(arg);
298 if (arg_offset < 0)
299 continue;
300 if (!is_pointer(arg))
301 star = 1;
302 goto found;
303 } END_FOR_EACH_PTR(arg);
305 return;
307 found:
308 type = get_type(unop);
309 if (type && type->type == SYM_PTR)
310 type = get_real_base_type(type);
311 offset = get_member_offset(type, ptr->member->name);
312 if (offset < 0)
313 return;
315 if (star)
316 snprintf(offset_str, sizeof(offset_str), "*(-%d+%d)", offset, arg_offset);
317 else
318 snprintf(offset_str, sizeof(offset_str), "-%d+%d", offset, arg_offset);
319 snprintf(param_str, sizeof(param_str), "$%d", i);
320 sql_insert_caller_info(call, CONTAINER, -1, offset_str, param_str);
323 static void db_func_def(const char *name, struct symbol *sym, char *key, char *value)
325 sval_t offset = {
326 .type = &int_ctype,
328 const char *arg_offset;
329 struct symbol *arg;
330 int star = 0;
331 int param;
332 int val;
333 int i;
335 if (name || sym)
336 return;
337 if (key[0] == '*') {
338 star = 1;
339 key += 2;
342 val = atoi(key);
343 if (val < -4095 || val >= 0)
344 return;
345 offset.value = -val;
346 arg_offset = strchr(key, '+');
347 if (!arg_offset)
348 return;
349 val = atoi(arg_offset + 1);
350 if (val > 4095 || val < 0)
351 return;
352 offset.value |= val << 16;
353 if (star)
354 offset.value |= 1ULL << 31;
356 if (value[0] != '$')
357 return;
359 param = atoi(value + 1);
361 i = -1;
362 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
363 i++;
364 if (param == i)
365 set_state(param_id, arg->ident->name, arg, alloc_estate_sval(offset));
366 } END_FOR_EACH_PTR(arg);
369 struct db_info {
370 struct symbol *param_sym;
371 const char *prev_key;
372 struct range_list *rl;
375 static void set_param_value(const char *key, struct symbol *sym, struct range_list *rl)
377 const char *name;
378 char fullname[256];
380 if (!sym || !sym->ident)
381 return;
383 name = sym->ident->name;
384 if (strcmp(key, "*$") == 0)
385 snprintf(fullname, sizeof(fullname), "*%s", name);
386 else if (key[0] == '$')
387 snprintf(fullname, 256, "%s%s", name, key + 1);
388 else
389 return;
391 set_state(SMATCH_EXTRA, fullname, sym, alloc_estate_rl(rl));
394 static int save_vals(void *_db_info, int argc, char **argv, char **azColName)
396 struct db_info *db_info = _db_info;
397 struct symbol *type;
398 struct range_list *rl;
399 const char *key;
400 const char *value;
402 if (argc == 2) {
403 key = argv[0];
404 value = argv[1];
405 } else {
406 key = "$";
407 value = argv[0];
410 if (db_info->prev_key &&
411 strcmp(db_info->prev_key, key) != 0) {
412 set_param_value(db_info->prev_key, db_info->param_sym, db_info->rl);
413 db_info->rl = NULL;
416 db_info->prev_key = alloc_sname(key);
418 type = get_member_type_from_key(symbol_expression(db_info->param_sym), key);
419 str_to_rl(type, (char *)value, &rl);
420 if (db_info->rl)
421 db_info->rl = rl_union(db_info->rl, rl);
422 else
423 db_info->rl = rl;
425 return 0;
428 static void load_tag_info_sym(mtag_t tag, struct symbol *sym, int arg_offset, int star)
430 struct db_info db_info = {
431 .param_sym = sym,
434 if (!tag)
435 return;
437 if (star) {
438 run_sql(save_vals, &db_info,
439 "select value from mtag_data where tag = %lld and offset = %d and type = %d;",
440 tag, arg_offset, DATA_VALUE);
441 } else {
442 run_sql(save_vals, &db_info,
443 "select data, value from mtag_data where tag = %lld and type = %d;",
444 tag, DATA_VALUE);
447 if (db_info.prev_key)
448 set_param_value(db_info.prev_key, db_info.param_sym, db_info.rl);
451 static void handle_passed_container(struct symbol *sym)
453 struct symbol *arg;
454 struct smatch_state *state;
455 mtag_t fn_tag, container_tag, arg_tag;
456 sval_t offset;
457 int container_offset, arg_offset;
458 int star;
460 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
461 state = get_state(param_id, arg->ident->name, arg);
462 if (state)
463 goto found;
464 } END_FOR_EACH_PTR(arg);
466 return;
467 found:
468 if (!estate_get_single_value(state, &offset))
469 return;
470 container_offset = -(offset.value & 0xffff);
471 arg_offset = -((offset.value & 0xfff0000) >> 16);
472 star = !!(offset.value & (1ULL << 31));
474 if (!get_toplevel_mtag(cur_func_sym, &fn_tag))
475 return;
476 if (!mtag_map_select_container(fn_tag, container_offset, &container_tag))
477 return;
478 if (!arg_offset || star) {
479 arg_tag = container_tag;
480 } else {
481 if (!mtag_map_select_tag(container_tag, arg_offset, &arg_tag))
482 return;
485 load_tag_info_sym(arg_tag, arg, -arg_offset, star);
488 void register_container_of(int id)
490 my_id = id;
492 add_hook(&match_function_def, FUNC_DEF_HOOK);
494 add_get_state_hook(&get_state_hook);
496 add_hook(&match_save_states, INLINE_FN_START);
497 add_hook(&match_restore_states, INLINE_FN_END);
499 select_call_implies_hook(CONTAINER, &set_param_used);
500 all_return_states_hook(&process_states);
502 add_split_return_callback(&print_returns_container_of);
503 select_return_states_hook(CONTAINER, &returns_container_of);
505 add_hook(&match_call, FUNCTION_CALL_HOOK);
508 static struct smatch_state *unmatched_state(struct sm_state *sm)
510 return alloc_estate_whole(estate_type(sm->state));
513 void register_container_of2(int id)
515 param_id = id;
517 select_caller_info_hook(db_func_def, CONTAINER);
518 add_hook(&handle_passed_container, AFTER_DEF_HOOK);
519 add_unmatched_state_hook(param_id, &unmatched_state);
520 add_merge_hook(param_id, &merge_estates);