equiv: don't mess with equiv in faked environments
[smatch.git] / smatch_container_of.c
blob098885be7da239744e6c50b308f785e963d3c0cb
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 int get_param_from_container_of(struct expression *expr)
27 struct expression *param_expr;
28 struct symbol *type;
29 sval_t sval;
30 int param;
33 type = get_type(expr);
34 if (!type || type->type != SYM_PTR)
35 return -1;
37 expr = strip_expr(expr);
38 if (expr->type != EXPR_BINOP || expr->op != '-')
39 return -1;
41 if (!get_value(expr->right, &sval))
42 return -1;
43 if (sval.value < 0 || sval.value > 4096)
44 return -1;
46 param_expr = get_assigned_expr(expr->left);
47 if (!param_expr)
48 return -1;
49 param = get_param_num(param_expr);
50 if (param < 0)
51 return -1;
53 return param;
56 int get_offset_from_container_of(struct expression *expr)
58 struct expression *param_expr;
59 struct symbol *type;
60 sval_t sval;
62 type = get_type(expr);
63 if (!type || type->type != SYM_PTR)
64 return -1;
66 expr = strip_expr(expr);
67 if (expr->type != EXPR_BINOP || expr->op != '-')
68 return -1;
70 if (!get_value(expr->right, &sval))
71 return -1;
72 if (sval.value < 0 || sval.value > 4096)
73 return -1;
75 param_expr = get_assigned_expr(expr->left);
76 if (!param_expr)
77 return -1;
79 return sval.value;
82 static void print_returns_container_of(int return_id, char *return_ranges, struct expression *expr)
84 int offset;
85 int param;
86 char key[64];
87 char value[64];
89 param = get_param_from_container_of(expr);
90 if (param < 0)
91 return;
92 offset = get_offset_from_container_of(expr);
93 if (offset < 0)
94 return;
96 snprintf(key, sizeof(key), "%d", param);
97 snprintf(value, sizeof(value), "-%d", offset);
99 /* no need to add it to return_implies because it's not really param_used */
100 sql_insert_return_states(return_id, return_ranges, CONTAINER, -1,
101 key, value);
104 static int get_deref_count(struct expression *expr)
106 int cnt = 0;
108 while (expr && expr->type == EXPR_DEREF) {
109 expr = expr->deref;
110 if (expr->type == EXPR_PREOP && expr->op == '*')
111 expr = expr->unop;
112 cnt++;
113 if (cnt > 100)
114 return -1;
116 return cnt;
119 static struct expression *get_partial_deref(struct expression *expr, int cnt)
121 while (--cnt >= 0) {
122 if (!expr || expr->type != EXPR_DEREF)
123 return expr;
124 expr = expr->deref;
125 if (expr->type == EXPR_PREOP && expr->op == '*')
126 expr = expr->unop;
128 return expr;
131 static int partial_deref_to_offset_str(struct expression *expr, int cnt, char op, char *buf, int size)
133 int n, offset;
135 if (cnt == 0)
136 return snprintf(buf, size, "%c0", op);
138 n = 0;
139 while (--cnt >= 0) {
140 offset = get_member_offset_from_deref(expr);
141 if (offset < 0)
142 return -1;
143 n += snprintf(buf + n, size - n, "%c%d", op, offset);
144 if (expr->type != EXPR_DEREF)
145 return -1;
146 expr = expr->deref;
147 if (expr->type == EXPR_PREOP && expr->op == '*')
148 expr = expr->unop;
151 return n;
154 static char *get_shared_str(struct expression *expr, struct expression *container)
156 struct expression *one, *two;
157 int exp, cont, min, ret, n;
158 static char buf[48];
160 exp = get_deref_count(expr);
161 cont = get_deref_count(container);
162 if (exp < 0 || cont < 0)
163 return NULL;
165 min = (exp < cont) ? exp : cont;
166 while (min >= 0) {
167 one = get_partial_deref(expr, exp - min);
168 two = get_partial_deref(container, cont - min);
169 if (expr_equiv(one, two))
170 goto found;
171 min--;
174 return NULL;
176 found:
177 ret = partial_deref_to_offset_str(expr, exp - min, '-', buf, sizeof(buf));
178 if (ret < 0)
179 return NULL;
180 n = ret;
181 ret = partial_deref_to_offset_str(container, cont - min, '+', buf + ret, sizeof(buf) - ret);
182 if (ret < 0)
183 return NULL;
184 n += ret;
185 if (n >= sizeof(buf))
186 return NULL;
188 return buf;
191 static char *get_stored_container_name(struct expression *container,
192 struct expression *expr)
194 struct smatch_state *state;
195 static char buf[64];
196 char *p;
197 int param;
199 if (!container || container->type != EXPR_SYMBOL)
200 return NULL;
201 if (!expr || expr->type != EXPR_SYMBOL)
202 return NULL;
203 state = get_state_expr(param_id, expr);
204 if (!state)
205 return NULL;
207 snprintf(buf, sizeof(buf), "%s", state->name);
208 p = strchr(buf, '|');
209 if (!p)
210 return NULL;
211 *p = '\0';
212 param = atoi(p + 2);
213 if (get_param_sym_from_num(param) == container->symbol)
214 return buf;
215 return NULL;
218 static char *get_container_name_helper(struct expression *container, struct expression *expr)
220 struct symbol *container_sym, *sym;
221 static char buf[64];
222 char *ret, *shared;
223 bool star;
225 expr = strip_expr(expr);
226 container = strip_expr(container);
227 if (!expr || !container)
228 return NULL;
230 ret = get_stored_container_name(container, expr);
231 if (ret)
232 return ret;
234 sym = expr_to_sym(expr);
235 container_sym = expr_to_sym(container);
236 if (!sym || !container_sym)
237 return NULL;
238 if (sym != container_sym)
239 return NULL;
241 if (container->type == EXPR_DEREF)
242 star = true;
243 else
244 star = false;
246 if (container->type == EXPR_PREOP && container->op == '&')
247 container = strip_expr(container->unop);
248 if (expr->type == EXPR_PREOP && expr->op == '&')
249 expr = strip_expr(expr->unop);
251 shared = get_shared_str(expr, container);
252 if (!shared)
253 return NULL;
254 if (star)
255 snprintf(buf, sizeof(buf), "*(%s)", shared);
256 else
257 snprintf(buf, sizeof(buf), "%s", shared);
259 return buf;
262 char *get_container_name(struct expression *container, struct expression *expr)
264 char *ret;
266 ret = get_container_name_helper(container, expr);
267 if (ret)
268 return ret;
270 ret = get_container_name_helper(get_assigned_expr(container), expr);
271 if (ret)
272 return ret;
274 ret = get_container_name_helper(container, get_assigned_expr(expr));
275 if (ret)
276 return ret;
278 ret = get_container_name_helper(get_assigned_expr(container),
279 get_assigned_expr(expr));
280 if (ret)
281 return ret;
283 return NULL;
286 static bool is_fn_ptr(struct expression *expr)
288 struct symbol *type;
290 if (!expr)
291 return false;
292 if (expr->type != EXPR_SYMBOL && expr->type != EXPR_DEREF)
293 return false;
295 type = get_type(expr);
296 if (!type || type->type != SYM_PTR)
297 return false;
298 type = get_real_base_type(type);
299 if (!type || type->type != SYM_FN)
300 return false;
301 return true;
304 static void match_call(struct expression *call)
306 struct expression *fn, *arg, *tmp;
307 bool found = false;
308 int fn_param, param;
309 char buf[32];
310 char *name;
313 * We're trying to link the function with the parameter. There are a
314 * couple ways this can be passed:
315 * foo->func(foo, ...);
316 * foo->func(foo->x, ...);
317 * foo->bar.func(&foo->bar, ...);
318 * foo->bar->baz->func(foo, ...);
320 * So the method is basically to subtract the offsets until we get to
321 * the common bit, then add the member offsets to get the parameter.
323 * If we're taking an address then the offset math is not stared,
324 * otherwise it is. Starred means dereferenced.
326 fn = strip_expr(call->fn);
328 param = -1;
329 FOR_EACH_PTR(call->args, arg) {
330 param++;
332 name = get_container_name(arg, fn);
333 if (!name)
334 continue;
336 found = true;
337 sql_insert_caller_info(call, CONTAINER, param, name, "$(-1)");
338 } END_FOR_EACH_PTR(arg);
340 if (found)
341 return;
343 fn_param = -1;
344 FOR_EACH_PTR(call->args, arg) {
345 fn_param++;
346 if (!is_fn_ptr(arg))
347 continue;
348 param = -1;
349 FOR_EACH_PTR(call->args, tmp) {
350 param++;
352 /* the function isn't it's own container */
353 if (arg == tmp)
354 continue;
356 name = get_container_name(tmp, arg);
357 if (!name)
358 continue;
360 snprintf(buf, sizeof(buf), "$%d", param);
361 sql_insert_caller_info(call, CONTAINER, fn_param, name, buf);
362 return;
363 } END_FOR_EACH_PTR(tmp);
364 } END_FOR_EACH_PTR(arg);
367 static void db_passed_container(const char *name, struct symbol *sym, char *key, char *value)
369 char buf[64];
371 snprintf(buf, sizeof(buf), "%s|%s", key, value);
372 set_state(param_id, name, sym, alloc_state_str(buf));
375 struct db_info {
376 struct symbol *arg;
377 int prev_offset;
378 struct range_list *rl;
379 int star;
380 struct stree *stree;
383 static struct symbol *get_member_from_offset(struct symbol *sym, int offset)
385 struct symbol *type, *tmp;
386 int cur;
388 type = get_real_base_type(sym);
389 if (!type || type->type != SYM_PTR)
390 return NULL;
391 type = get_real_base_type(type);
392 if (!type || type->type != SYM_STRUCT)
393 return NULL;
395 cur = 0;
396 FOR_EACH_PTR(type->symbol_list, tmp) {
397 cur = ALIGN(cur, tmp->ctype.alignment);
398 if (offset == cur)
399 return tmp;
400 cur += type_bytes(tmp);
401 } END_FOR_EACH_PTR(tmp);
402 return NULL;
405 static struct symbol *get_member_type_from_offset(struct symbol *sym, int offset)
407 struct symbol *base_type;
408 struct symbol *member;
410 base_type = get_real_base_type(sym);
411 if (base_type && base_type->type == SYM_PTR)
412 base_type = get_real_base_type(base_type);
413 if (offset == 0 && base_type && base_type->type == SYM_BASETYPE)
414 return base_type;
416 member = get_member_from_offset(sym, offset);
417 if (!member)
418 return NULL;
419 return get_real_base_type(member);
422 static const char *get_name_from_offset(struct symbol *arg, int offset)
424 struct symbol *member, *type;
425 const char *name;
426 static char fullname[256];
428 name = arg->ident->name;
430 type = get_real_base_type(arg);
431 if (!type || type->type != SYM_PTR)
432 return name;
434 type = get_real_base_type(type);
435 if (!type)
436 return NULL;
437 if (type->type != SYM_STRUCT) {
438 snprintf(fullname, sizeof(fullname), "*%s", name);
439 return fullname;
442 member = get_member_from_offset(arg, offset);
443 if (!member || !member->ident)
444 return NULL;
446 snprintf(fullname, sizeof(fullname), "%s->%s", name, member->ident->name);
447 return fullname;
450 static void set_param_value(struct stree **stree, struct symbol *arg, int offset, struct range_list *rl)
452 const char *name;
454 name = get_name_from_offset(arg, offset);
455 if (!name)
456 return;
457 set_state_stree(stree, SMATCH_EXTRA, name, arg, alloc_estate_rl(rl));
460 static int save_vals(void *_db_info, int argc, char **argv, char **azColName)
462 struct db_info *db_info = _db_info;
463 struct symbol *type;
464 struct range_list *rl;
465 int offset = 0;
466 const char *value;
468 if (argc == 2) {
469 offset = atoi(argv[0]);
470 value = argv[1];
471 } else {
472 value = argv[0];
475 if (db_info->prev_offset != -1 &&
476 db_info->prev_offset != offset) {
477 set_param_value(&db_info->stree, db_info->arg, db_info->prev_offset, db_info->rl);
478 db_info->rl = NULL;
481 db_info->prev_offset = offset;
483 type = get_real_base_type(db_info->arg);
484 if (db_info->star)
485 goto found_type;
486 if (type->type != SYM_PTR)
487 return 0;
488 type = get_real_base_type(type);
489 if (type->type == SYM_BASETYPE)
490 goto found_type;
491 type = get_member_type_from_offset(db_info->arg, offset);
492 found_type:
493 str_to_rl(type, (char *)value, &rl);
494 if (db_info->rl)
495 db_info->rl = rl_union(db_info->rl, rl);
496 else
497 db_info->rl = rl;
499 return 0;
502 static struct stree *load_tag_info_sym(mtag_t tag, struct symbol *arg, int arg_offset, int star)
504 struct db_info db_info = {
505 .arg = arg,
506 .prev_offset = -1,
507 .star = star,
509 struct symbol *type;
511 if (!tag || !arg->ident)
512 return NULL;
514 type = get_real_base_type(arg);
515 if (!type)
516 return NULL;
517 if (!star) {
518 if (type->type != SYM_PTR)
519 return NULL;
520 type = get_real_base_type(type);
521 if (!type)
522 return NULL;
525 if (star || type->type == SYM_BASETYPE) {
526 run_sql(save_vals, &db_info,
527 "select value from mtag_data where tag = %lld and offset = %d and type = %d;",
528 tag, arg_offset, DATA_VALUE);
529 } else { /* presumably the parameter is a struct pointer */
530 run_sql(save_vals, &db_info,
531 "select offset, value from mtag_data where tag = %lld and type = %d order by offset;",
532 tag, DATA_VALUE);
535 if (db_info.prev_offset != -1)
536 set_param_value(&db_info.stree, arg, db_info.prev_offset, db_info.rl);
538 // FIXME: handle an offset correctly
539 if (!star && !arg_offset) {
540 sval_t sval;
542 sval.type = get_real_base_type(arg);
543 sval.uvalue = tag;
544 set_state_stree(&db_info.stree, SMATCH_EXTRA, arg->ident->name, arg, alloc_estate_sval(sval));
546 return db_info.stree;
549 static void load_container_data(struct symbol *arg, const char *info)
551 mtag_t cur_tag, container_tag, arg_tag;
552 int container_offset, arg_offset;
553 struct sm_state *sm;
554 struct stree *stree;
555 char *p, *cont;
556 char copy[64];
557 bool star = 0;
559 snprintf(copy, sizeof(copy), "%s", info);
560 p = strchr(copy, '|');
561 if (!p)
562 return;
563 *p = '\0';
564 cont = p + 1;
565 p = copy;
566 if (p[0] == '*') {
567 star = 1;
568 p += 2;
571 if (strcmp(cont, "$(-1)") != 0)
572 return;
574 if (!get_toplevel_mtag(cur_func_sym, &cur_tag))
575 return;
577 while (true) {
578 container_offset = strtoul(p, &p, 0);
579 if (local_debug)
580 sm_msg("%s: cur_tag = %llu container_offset = %d",
581 __func__, cur_tag, container_offset);
582 if (!mtag_map_select_container(cur_tag, -container_offset, &container_tag))
583 return;
584 cur_tag = container_tag;
585 if (local_debug)
586 sm_msg("%s: container_tag = %llu p = '%s'",
587 __func__, container_tag, p);
588 if (!p)
589 return;
590 if (p[0] != '-')
591 break;
592 p++;
595 if (p[0] != '+')
596 return;
598 p++;
599 arg_offset = strtoul(p, &p, 0);
600 if (p && *p && *p != ')')
601 return;
603 if (!arg_offset || star) {
604 arg_tag = container_tag;
605 } else {
606 if (!mtag_map_select_tag(container_tag, arg_offset, &arg_tag))
607 return;
610 stree = load_tag_info_sym(arg_tag, arg, arg_offset, star);
611 FOR_EACH_SM(stree, sm) {
612 set_state(sm->owner, sm->name, sm->sym, sm->state);
613 } END_FOR_EACH_SM(sm);
614 free_stree(&stree);
617 static void handle_passed_container(struct symbol *sym)
619 struct symbol *arg;
620 struct smatch_state *state;
622 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
623 state = get_state(param_id, arg->ident->name, arg);
624 if (!state || state == &merged)
625 continue;
626 load_container_data(arg, state->name);
627 } END_FOR_EACH_PTR(arg);
630 void register_container_of(int id)
632 my_id = id;
634 add_split_return_callback(&print_returns_container_of);
635 add_hook(&match_call, FUNCTION_CALL_HOOK);
638 void register_container_of2(int id)
640 param_id = id;
642 set_dynamic_states(param_id);
643 select_caller_info_hook(db_passed_container, CONTAINER);
644 add_merge_hook(param_id, &merge_str_state);
645 add_hook(&handle_passed_container, AFTER_DEF_HOOK);