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 int get_param_from_container_of(struct expression
*expr
)
27 struct expression
*param_expr
;
33 type
= get_type(expr
);
34 if (!type
|| type
->type
!= SYM_PTR
)
37 expr
= strip_expr(expr
);
38 if (expr
->type
!= EXPR_BINOP
|| expr
->op
!= '-')
41 if (!get_value(expr
->right
, &sval
))
43 if (sval
.value
< 0 || sval
.value
> 4096)
46 param_expr
= get_assigned_expr(expr
->left
);
49 param
= get_param_num(param_expr
);
56 int get_offset_from_container_of(struct expression
*expr
)
58 struct expression
*param_expr
;
62 type
= get_type(expr
);
63 if (!type
|| type
->type
!= SYM_PTR
)
66 expr
= strip_expr(expr
);
67 if (expr
->type
!= EXPR_BINOP
|| expr
->op
!= '-')
70 if (!get_value(expr
->right
, &sval
))
72 if (sval
.value
< 0 || sval
.value
> 4096)
75 param_expr
= get_assigned_expr(expr
->left
);
82 static void print_returns_container_of(int return_id
, char *return_ranges
, struct expression
*expr
)
89 param
= get_param_from_container_of(expr
);
92 offset
= get_offset_from_container_of(expr
);
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,
104 static int get_deref_count(struct expression
*expr
)
108 while (expr
&& expr
->type
== EXPR_DEREF
) {
110 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
119 static struct expression
*get_partial_deref(struct expression
*expr
, int cnt
)
122 if (!expr
|| expr
->type
!= EXPR_DEREF
)
125 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
131 static int partial_deref_to_offset_str(struct expression
*expr
, int cnt
, char op
, char *buf
, int size
)
136 return snprintf(buf
, size
, "%c0", op
);
140 offset
= get_member_offset_from_deref(expr
);
143 n
+= snprintf(buf
+ n
, size
- n
, "%c%d", op
, offset
);
144 if (expr
->type
!= EXPR_DEREF
)
147 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
154 static char *get_shared_str(struct expression
*expr
, struct expression
*container
)
156 struct expression
*one
, *two
;
157 int exp
, cont
, min
, ret
, n
;
160 exp
= get_deref_count(expr
);
161 cont
= get_deref_count(container
);
162 if (exp
< 0 || cont
< 0)
165 min
= (exp
< cont
) ? exp
: cont
;
167 one
= get_partial_deref(expr
, exp
- min
);
168 two
= get_partial_deref(container
, cont
- min
);
169 if (expr_equiv(one
, two
))
177 ret
= partial_deref_to_offset_str(expr
, exp
- min
, '-', buf
, sizeof(buf
));
181 ret
= partial_deref_to_offset_str(container
, cont
- min
, '+', buf
+ ret
, sizeof(buf
) - ret
);
185 if (n
>= sizeof(buf
))
191 static char *get_stored_container_name(struct expression
*container
,
192 struct expression
*expr
)
194 struct smatch_state
*state
;
199 if (!container
|| container
->type
!= EXPR_SYMBOL
)
201 if (!expr
|| expr
->type
!= EXPR_SYMBOL
)
203 state
= get_state_expr(param_id
, expr
);
207 snprintf(buf
, sizeof(buf
), "%s", state
->name
);
208 p
= strchr(buf
, '|');
213 if (get_param_sym_from_num(param
) == container
->symbol
)
218 static char *get_container_name_helper(struct expression
*container
, struct expression
*expr
)
220 struct symbol
*container_sym
, *sym
;
225 expr
= strip_expr(expr
);
226 container
= strip_expr(container
);
227 if (!expr
|| !container
)
230 ret
= get_stored_container_name(container
, expr
);
234 sym
= expr_to_sym(expr
);
235 container_sym
= expr_to_sym(container
);
236 if (!sym
|| !container_sym
)
238 if (sym
!= container_sym
)
241 if (container
->type
== EXPR_DEREF
)
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
);
255 snprintf(buf
, sizeof(buf
), "*(%s)", shared
);
257 snprintf(buf
, sizeof(buf
), "%s", shared
);
262 char *get_container_name(struct expression
*container
, struct expression
*expr
)
266 ret
= get_container_name_helper(container
, expr
);
270 ret
= get_container_name_helper(get_assigned_expr(container
), expr
);
274 ret
= get_container_name_helper(container
, get_assigned_expr(expr
));
278 ret
= get_container_name_helper(get_assigned_expr(container
),
279 get_assigned_expr(expr
));
286 static bool is_fn_ptr(struct expression
*expr
)
292 if (expr
->type
!= EXPR_SYMBOL
&& expr
->type
!= EXPR_DEREF
)
295 type
= get_type(expr
);
296 if (!type
|| type
->type
!= SYM_PTR
)
298 type
= get_real_base_type(type
);
299 if (!type
|| type
->type
!= SYM_FN
)
304 static void match_call(struct expression
*call
)
306 struct expression
*fn
, *arg
, *tmp
;
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
);
329 FOR_EACH_PTR(call
->args
, arg
) {
332 name
= get_container_name(arg
, fn
);
337 sql_insert_caller_info(call
, CONTAINER
, param
, name
, "$(-1)");
338 } END_FOR_EACH_PTR(arg
);
344 FOR_EACH_PTR(call
->args
, arg
) {
349 FOR_EACH_PTR(call
->args
, tmp
) {
352 /* the function isn't it's own container */
356 name
= get_container_name(tmp
, arg
);
360 snprintf(buf
, sizeof(buf
), "$%d", param
);
361 sql_insert_caller_info(call
, CONTAINER
, fn_param
, name
, buf
);
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
)
371 snprintf(buf
, sizeof(buf
), "%s|%s", key
, value
);
372 set_state(param_id
, name
, sym
, alloc_state_str(buf
));
378 struct range_list
*rl
;
383 static struct symbol
*get_member_from_offset(struct symbol
*sym
, int offset
)
385 struct symbol
*type
, *tmp
;
388 type
= get_real_base_type(sym
);
389 if (!type
|| type
->type
!= SYM_PTR
)
391 type
= get_real_base_type(type
);
392 if (!type
|| type
->type
!= SYM_STRUCT
)
396 FOR_EACH_PTR(type
->symbol_list
, tmp
) {
397 cur
= ALIGN(cur
, tmp
->ctype
.alignment
);
400 cur
+= type_bytes(tmp
);
401 } END_FOR_EACH_PTR(tmp
);
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
)
416 member
= get_member_from_offset(sym
, offset
);
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
;
426 static char fullname
[256];
428 name
= arg
->ident
->name
;
430 type
= get_real_base_type(arg
);
431 if (!type
|| type
->type
!= SYM_PTR
)
434 type
= get_real_base_type(type
);
437 if (type
->type
!= SYM_STRUCT
) {
438 snprintf(fullname
, sizeof(fullname
), "*%s", name
);
442 member
= get_member_from_offset(arg
, offset
);
443 if (!member
|| !member
->ident
)
446 snprintf(fullname
, sizeof(fullname
), "%s->%s", name
, member
->ident
->name
);
450 static void set_param_value(struct stree
**stree
, struct symbol
*arg
, int offset
, struct range_list
*rl
)
454 name
= get_name_from_offset(arg
, offset
);
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
;
464 struct range_list
*rl
;
469 offset
= atoi(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
);
481 db_info
->prev_offset
= offset
;
483 type
= get_real_base_type(db_info
->arg
);
486 if (type
->type
!= SYM_PTR
)
488 type
= get_real_base_type(type
);
489 if (type
->type
== SYM_BASETYPE
)
491 type
= get_member_type_from_offset(db_info
->arg
, offset
);
493 str_to_rl(type
, (char *)value
, &rl
);
495 db_info
->rl
= rl_union(db_info
->rl
, rl
);
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
= {
511 if (!tag
|| !arg
->ident
)
514 type
= get_real_base_type(arg
);
518 if (type
->type
!= SYM_PTR
)
520 type
= get_real_base_type(type
);
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;",
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
) {
542 sval
.type
= get_real_base_type(arg
);
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
;
559 snprintf(copy
, sizeof(copy
), "%s", info
);
560 p
= strchr(copy
, '|');
571 if (strcmp(cont
, "$(-1)") != 0)
574 if (!get_toplevel_mtag(cur_func_sym
, &cur_tag
))
578 container_offset
= strtoul(p
, &p
, 0);
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
))
584 cur_tag
= container_tag
;
586 sm_msg("%s: container_tag = %llu p = '%s'",
587 __func__
, container_tag
, p
);
599 arg_offset
= strtoul(p
, &p
, 0);
600 if (p
&& *p
&& *p
!= ')')
603 if (!arg_offset
|| star
) {
604 arg_tag
= container_tag
;
606 if (!mtag_map_select_tag(container_tag
, arg_offset
, &arg_tag
))
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
);
617 static void handle_passed_container(struct symbol
*sym
)
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
)
626 load_container_data(arg
, state
->name
);
627 } END_FOR_EACH_PTR(arg
);
630 void register_container_of(int 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
)
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
);