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
19 #include "smatch_slist.h"
20 #include "smatch_extra.h"
25 static struct stree
*used_stree
;
26 static struct stree_stack
*saved_stack
;
30 int get_param_from_container_of(struct expression
*expr
)
32 struct expression
*param_expr
;
38 type
= get_type(expr
);
39 if (!type
|| type
->type
!= SYM_PTR
)
42 expr
= strip_expr(expr
);
43 if (expr
->type
!= EXPR_BINOP
|| expr
->op
!= '-')
46 if (!get_value(expr
->right
, &sval
))
48 if (sval
.value
< 0 || sval
.value
> 4096)
51 param_expr
= get_assigned_expr(expr
->left
);
54 param
= get_param_num(param_expr
);
61 int get_offset_from_container_of(struct expression
*expr
)
63 struct expression
*param_expr
;
67 type
= get_type(expr
);
68 if (!type
|| type
->type
!= SYM_PTR
)
71 expr
= strip_expr(expr
);
72 if (expr
->type
!= EXPR_BINOP
|| expr
->op
!= '-')
75 if (!get_value(expr
->right
, &sval
))
77 if (sval
.value
< 0 || sval
.value
> 4096)
80 param_expr
= get_assigned_expr(expr
->left
);
87 static int get_container_arg(struct symbol
*sym
)
89 struct expression
*__mptr
;
92 if (!sym
|| !sym
->ident
)
95 __mptr
= get_assigned_expr_name_sym(sym
->ident
->name
, sym
);
96 param
= get_param_from_container_of(__mptr
);
101 static int get_container_offset(struct symbol
*sym
)
103 struct expression
*__mptr
;
106 if (!sym
|| !sym
->ident
)
109 __mptr
= get_assigned_expr_name_sym(sym
->ident
->name
, sym
);
110 offset
= get_offset_from_container_of(__mptr
);
115 static char *get_container_name_sm(struct sm_state
*sm
, int offset
)
117 static char buf
[256];
120 name
= get_param_name(sm
);
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);
134 static void get_state_hook(int owner
, const char *name
, struct symbol
*sym
)
140 if (__in_fake_assign
)
143 arg
= get_container_arg(sym
);
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
)
154 name
= get_variable_from_key(arg
, key
, &sym
);
158 arg_nr
= get_container_arg(sym
);
160 set_state(my_id
, name
, sym
, &used
);
165 static void process_states(void)
167 struct sm_state
*tmp
;
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)
176 name
= get_container_name_sm(tmp
, offset
);
179 sql_insert_return_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
);
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
)
209 param
= get_param_from_container_of(expr
);
212 offset
= get_offset_from_container_of(expr
);
216 snprintf(key
, sizeof(key
), "%d", param
);
217 snprintf(value
, sizeof(value
), "-%d", offset
);
219 /* no need to add it to return_implies because it's not really param_used */
220 sql_insert_return_states(return_id
, return_ranges
, CONTAINER
, -1,
224 static void returns_container_of(struct expression
*expr
, int param
, char *key
, char *value
)
226 struct expression
*call
, *arg
;
230 if (expr
->type
!= EXPR_ASSIGNMENT
|| expr
->op
!= '=')
232 call
= strip_expr(expr
->right
);
233 if (call
->type
!= EXPR_CALL
)
238 offset
= atoi(value
);
240 arg
= get_argument_from_call_expr(call
->args
, param
);
243 if (arg
->type
!= EXPR_SYMBOL
)
245 param
= get_param_num(arg
);
248 snprintf(buf
, sizeof(buf
), "$(%d)", offset
);
249 sql_insert_return_implies(CONTAINER
, param
, buf
, "");
252 static int get_deref_count(struct expression
*expr
)
256 while (expr
&& expr
->type
== EXPR_DEREF
) {
258 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
267 static struct expression
*get_partial_deref(struct expression
*expr
, int cnt
)
270 if (!expr
|| expr
->type
!= EXPR_DEREF
)
273 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
279 static int partial_deref_to_offset_str(struct expression
*expr
, int cnt
, char op
, char *buf
, int size
)
284 return snprintf(buf
, size
, "%c0", op
);
288 offset
= get_member_offset_from_deref(expr
);
291 n
+= snprintf(buf
+ n
, size
- n
, "%c%d", op
, offset
);
292 if (expr
->type
!= EXPR_DEREF
)
295 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
302 static char *get_shared_str(struct expression
*container
, struct expression
*expr
)
304 struct expression
*one
, *two
;
305 int cont
, exp
, min
, ret
, n
;
308 cont
= get_deref_count(container
);
309 exp
= get_deref_count(expr
);
310 if (cont
< 0 || exp
< 0)
313 min
= (cont
< exp
) ? cont
: exp
;
315 one
= get_partial_deref(container
, cont
- min
);
316 two
= get_partial_deref(expr
, exp
- min
);
317 if (expr_equiv(one
, two
))
325 ret
= partial_deref_to_offset_str(container
, cont
- min
, '-', buf
, sizeof(buf
));
329 ret
= partial_deref_to_offset_str(expr
, exp
- min
, '+', buf
+ ret
, sizeof(buf
) - ret
);
333 if (n
>= sizeof(buf
))
339 char *get_container_name(struct expression
*container
, struct expression
*expr
)
341 struct symbol
*container_sym
, *sym
;
346 expr
= strip_expr(expr
);
348 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '&') {
349 expr
= strip_expr(expr
->unop
);
353 container_sym
= expr_to_sym(container
);
356 sym
= expr_to_sym(expr
);
357 if (!sym
|| container_sym
!= sym
)
360 shared
= get_shared_str(container
, expr
);
362 snprintf(buf
, sizeof(buf
), "*(%s)", shared
);
364 snprintf(buf
, sizeof(buf
), "%s", shared
);
369 static void match_call(struct expression
*call
)
371 struct expression
*fn
, *arg
;
376 * We're trying to link the function with the parameter. There are a
377 * couple ways this can be passed:
378 * foo->func(foo, ...);
379 * foo->func(foo->x, ...);
380 * foo->bar.func(&foo->bar, ...);
381 * foo->bar->baz->func(foo, ...);
383 * So the method is basically to subtract the offsets until we get to
384 * the common bit, then add the member offsets to get the parameter.
386 * If we're taking an address then the offset math is not stared,
387 * otherwise it is. Starred means dereferenced.
389 fn
= strip_expr(call
->fn
);
392 FOR_EACH_PTR(call
->args
, arg
) {
395 name
= get_container_name(fn
, arg
);
399 sql_insert_caller_info(call
, CONTAINER
, param
, name
, "$(-1)");
400 } END_FOR_EACH_PTR(arg
);
403 static void db_passed_container(const char *name
, struct symbol
*sym
, char *key
, char *value
)
405 set_state(param_id
, name
, sym
, alloc_state_str(key
));
411 struct range_list
*rl
;
416 static struct symbol
*get_member_from_offset(struct symbol
*sym
, int offset
)
418 struct symbol
*type
, *tmp
;
421 type
= get_real_base_type(sym
);
422 if (!type
|| type
->type
!= SYM_PTR
)
424 type
= get_real_base_type(type
);
425 if (!type
|| type
->type
!= SYM_STRUCT
)
429 FOR_EACH_PTR(type
->symbol_list
, tmp
) {
430 cur
= ALIGN(cur
, tmp
->ctype
.alignment
);
433 cur
+= type_bytes(tmp
);
434 } END_FOR_EACH_PTR(tmp
);
438 static struct symbol
*get_member_type_from_offset(struct symbol
*sym
, int offset
)
440 struct symbol
*base_type
;
441 struct symbol
*member
;
443 base_type
= get_real_base_type(sym
);
444 if (base_type
&& base_type
->type
== SYM_PTR
)
445 base_type
= get_real_base_type(base_type
);
446 if (offset
== 0 && base_type
&& base_type
->type
== SYM_BASETYPE
)
449 member
= get_member_from_offset(sym
, offset
);
452 return get_real_base_type(member
);
455 static const char *get_name_from_offset(struct symbol
*arg
, int offset
)
457 struct symbol
*member
, *type
;
459 static char fullname
[256];
461 name
= arg
->ident
->name
;
463 type
= get_real_base_type(arg
);
464 if (!type
|| type
->type
!= SYM_PTR
)
467 type
= get_real_base_type(type
);
470 if (type
->type
!= SYM_STRUCT
) {
471 snprintf(fullname
, sizeof(fullname
), "*%s", name
);
475 member
= get_member_from_offset(arg
, offset
);
479 snprintf(fullname
, sizeof(fullname
), "%s->%s", name
, member
->ident
->name
);
483 static void set_param_value(struct stree
**stree
, struct symbol
*arg
, int offset
, struct range_list
*rl
)
487 name
= get_name_from_offset(arg
, offset
);
490 set_state_stree(stree
, SMATCH_EXTRA
, name
, arg
, alloc_estate_rl(rl
));
493 static int save_vals(void *_db_info
, int argc
, char **argv
, char **azColName
)
495 struct db_info
*db_info
= _db_info
;
497 struct range_list
*rl
;
502 offset
= atoi(argv
[0]);
508 if (db_info
->prev_offset
!= -1 &&
509 db_info
->prev_offset
!= offset
) {
510 set_param_value(&db_info
->stree
, db_info
->arg
, db_info
->prev_offset
, db_info
->rl
);
514 db_info
->prev_offset
= offset
;
516 type
= get_real_base_type(db_info
->arg
);
519 if (type
->type
!= SYM_PTR
)
521 type
= get_real_base_type(type
);
522 if (type
->type
== SYM_BASETYPE
)
524 type
= get_member_type_from_offset(db_info
->arg
, offset
);
526 str_to_rl(type
, (char *)value
, &rl
);
528 db_info
->rl
= rl_union(db_info
->rl
, rl
);
535 static struct stree
*load_tag_info_sym(mtag_t tag
, struct symbol
*arg
, int arg_offset
, int star
)
537 struct db_info db_info
= {
544 if (!tag
|| !arg
->ident
)
547 type
= get_real_base_type(arg
);
551 if (type
->type
!= SYM_PTR
)
553 type
= get_real_base_type(type
);
558 if (star
|| type
->type
== SYM_BASETYPE
) {
559 run_sql(save_vals
, &db_info
,
560 "select value from mtag_data where tag = %lld and offset = %d and type = %d;",
561 tag
, arg_offset
, DATA_VALUE
);
562 } else { /* presumably the parameter is a struct pointer */
563 run_sql(save_vals
, &db_info
,
564 "select offset, value from mtag_data where tag = %lld and type = %d;",
568 if (db_info
.prev_offset
!= -1)
569 set_param_value(&db_info
.stree
, arg
, db_info
.prev_offset
, db_info
.rl
);
571 // FIXME: handle an offset correctly
572 if (!star
&& !arg_offset
) {
575 sval
.type
= get_real_base_type(arg
);
577 set_state_stree(&db_info
.stree
, SMATCH_EXTRA
, arg
->ident
->name
, arg
, alloc_estate_sval(sval
));
579 return db_info
.stree
;
582 static void load_container_data(struct symbol
*arg
, const char *info
)
584 mtag_t fn_tag
, container_tag
, arg_tag
;
585 int container_offset
, arg_offset
;
586 char *p
= (char *)info
;
596 container_offset
= strtoul(p
, &p
, 0);
597 if (p
[0] != '+') /* FIXME: Handle multiple *(-4-34+48+246) */
601 arg_offset
= strtoul(p
, &p
, 0);
602 if (p
&& *p
&& *p
!= ')')
605 if (!get_toplevel_mtag(cur_func_sym
, &fn_tag
))
607 if (!mtag_map_select_container(fn_tag
, container_offset
, &container_tag
))
609 if (!arg_offset
|| star
) {
610 arg_tag
= container_tag
;
612 if (!mtag_map_select_tag(container_tag
, -arg_offset
, &arg_tag
))
616 stree
= load_tag_info_sym(arg_tag
, arg
, arg_offset
, star
);
617 FOR_EACH_SM(stree
, sm
) {
618 set_state(sm
->owner
, sm
->name
, sm
->sym
, sm
->state
);
619 } END_FOR_EACH_SM(sm
);
623 static void handle_passed_container(struct symbol
*sym
)
626 struct smatch_state
*state
;
628 FOR_EACH_PTR(cur_func_sym
->ctype
.base_type
->arguments
, arg
) {
629 state
= get_state(param_id
, arg
->ident
->name
, arg
);
630 if (!state
|| state
== &merged
)
632 load_container_data(arg
, state
->name
);
633 } END_FOR_EACH_PTR(arg
);
636 void register_container_of(int id
)
640 add_hook(&match_function_def
, FUNC_DEF_HOOK
);
642 add_get_state_hook(&get_state_hook
);
644 add_hook(&match_save_states
, INLINE_FN_START
);
645 add_hook(&match_restore_states
, INLINE_FN_END
);
647 select_return_implies_hook(CONTAINER
, &set_param_used
);
648 all_return_states_hook(&process_states
);
650 add_split_return_callback(&print_returns_container_of
);
651 select_return_states_hook(CONTAINER
, &returns_container_of
);
653 add_hook(&match_call
, FUNCTION_CALL_HOOK
);
656 void register_container_of2(int id
)
660 set_dynamic_states(param_id
);
661 select_caller_info_hook(db_passed_container
, CONTAINER
);
662 add_merge_hook(param_id
, &merge_str_state
);
663 add_hook(&handle_passed_container
, AFTER_DEF_HOOK
);