2 * Copyright (C) 2017 Oracle. All rights reserved.
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 * One problem that I have is that it's really hard to track how pointers are
20 * passed around. For example, it would be nice to know that the probe() and
21 * remove() functions get the same pci_dev pointer. It would be good to know
22 * what pointers we're passing to the open() and close() functions. But that
23 * information gets lost in a call tree full of function pointer calls.
25 * I think the first step is to start naming specific pointers. So when a
26 * pointer is allocated, then it gets a tag. So calls to kmalloc() generate a
27 * tag. But we might not use that, because there might be a better name like
28 * framebuffer_alloc(). The framebuffer_alloc() is interesting because there is
29 * one per driver and it's passed around to all the file operations.
31 * Perhaps we could make a list of functions like framebuffer_alloc() which take
32 * a size and say that those are the interesting alloc functions.
34 * Another place where we would maybe name the pointer is when they are passed
35 * to the probe(). Because that's an important pointer, since there is one
36 * per driver (sort of).
38 * My vision is that you could take a pointer and trace it back to a global. So
39 * I'm going to track that pointer_tag - 28 bytes takes you to another pointer
40 * tag. You could follow that one back and so on. Also when we pass a pointer
41 * to a function that would be recorded as sort of a link or path or something.
46 #include "smatch_slist.h"
47 #include "smatch_extra.h"
49 #include <openssl/md5.h>
53 static struct smatch_state
*alloc_tag_state(mtag_t tag
)
55 struct smatch_state
*state
;
58 state
= __alloc_smatch_state(0);
59 snprintf(buf
, sizeof(buf
), "%lld", tag
);
60 state
->name
= alloc_sname(buf
);
61 state
->data
= malloc(sizeof(mtag_t
));
62 *(mtag_t
*)state
->data
= tag
;
67 static mtag_t
str_to_tag(const char *str
)
69 unsigned char c
[MD5_DIGEST_LENGTH
];
70 unsigned long long *tag
= (unsigned long long *)&c
;
76 MD5_Update(&mdContext
, str
, len
);
77 MD5_Final(c
, &mdContext
);
79 *tag
&= ~MTAG_ALIAS_BIT
;
80 *tag
&= ~MTAG_OFFSET_MASK
;
85 static void alloc_assign(const char *fn
, struct expression
*expr
, void *unused
)
87 struct expression
*left
, *right
;
88 char *left_name
, *right_name
;
89 struct symbol
*left_sym
;
94 // FIXME: This should only happen when the size is not a paramter of
98 if (expr
->type
!= EXPR_ASSIGNMENT
|| expr
->op
!= '=')
100 left
= strip_expr(expr
->left
);
101 right
= strip_expr(expr
->right
);
102 if (right
->type
!= EXPR_CALL
|| right
->fn
->type
!= EXPR_SYMBOL
)
105 left_name
= expr_to_str_sym(left
, &left_sym
);
106 right_name
= expr_to_str(right
);
108 snprintf(buf
, sizeof(buf
), "%s %s %s %s", get_filename(), get_function(),
109 left_name
, right_name
);
110 tag
= str_to_tag(buf
);
112 sql_insert_mtag_about(tag
, left_name
, right_name
);
114 if (left_name
&& left_sym
)
115 set_state(my_id
, left_name
, left_sym
, alloc_tag_state(tag
));
117 free_string(left_name
);
118 free_string(right_name
);
121 int get_string_mtag(struct expression
*expr
, mtag_t
*tag
)
125 if (expr
->type
!= EXPR_STRING
|| !expr
->string
)
128 /* I was worried about collisions so I added a xor */
129 xor = str_to_tag("__smatch string");
130 *tag
= str_to_tag(expr
->string
->data
);
136 int get_toplevel_mtag(struct symbol
*sym
, mtag_t
*tag
)
144 !(sym
->ctype
.modifiers
& MOD_TOPLEVEL
))
147 snprintf(buf
, sizeof(buf
), "%s %s",
148 (sym
->ctype
.modifiers
& MOD_STATIC
) ? get_filename() : "extern",
150 *tag
= str_to_tag(buf
);
154 int get_deref_mtag(struct expression
*expr
, mtag_t
*tag
)
156 mtag_t container_tag
, member_tag
;
160 * I'm not totally sure what I'm doing...
162 * This is supposed to get something like "global_var->ptr", but I don't
163 * feel like it's complete at all.
167 if (!get_mtag(expr
->unop
, &container_tag
))
170 offset
= get_member_offset_from_deref(expr
);
174 if (!mtag_map_select_tag(container_tag
, -offset
, &member_tag
))
181 static void global_variable(struct symbol
*sym
)
185 if (!get_toplevel_mtag(sym
, &tag
))
188 sql_insert_mtag_about(tag
,
190 (sym
->ctype
.modifiers
& MOD_STATIC
) ? get_filename() : "extern");
193 static void db_returns_buf_size(struct expression
*expr
, int param
, char *unused
, char *math
)
195 struct expression
*call
;
196 struct range_list
*rl
;
198 if (expr
->type
!= EXPR_ASSIGNMENT
)
200 call
= strip_expr(expr
->right
);
202 if (!parse_call_math_rl(call
, math
, &rl
))
204 // rl = cast_rl(&int_ctype, rl);
205 // set_state_expr(my_size_id, expr->left, alloc_estate_rl(rl));
208 static void db_returns_memory_tag(struct expression
*expr
, int param
, char *key
, char *value
)
210 struct expression
*call
, *arg
;
215 call
= strip_expr(expr
);
216 while (call
->type
== EXPR_ASSIGNMENT
)
217 call
= strip_expr(call
->right
);
218 if (call
->type
!= EXPR_CALL
)
221 tag
= strtoul(value
, NULL
, 10);
223 if (!create_mtag_alias(tag
, call
, &alias
))
226 arg
= get_argument_from_call_expr(call
->args
, param
);
230 name
= get_variable_from_key(arg
, key
, &sym
);
234 set_state(my_id
, name
, sym
, alloc_tag_state(alias
));
239 static void match_call_info(struct expression
*expr
)
241 struct smatch_state
*state
;
242 struct expression
*arg
;
245 FOR_EACH_PTR(expr
->args
, arg
) {
247 state
= get_state_expr(my_id
, arg
);
248 if (!state
|| !state
->data
)
250 sql_insert_caller_info(expr
, MEMORY_TAG
, i
, "$", state
->name
);
251 } END_FOR_EACH_PTR(arg
);
254 static void save_caller_info(const char *name
, struct symbol
*sym
, char *key
, char *value
)
256 struct smatch_state
*state
;
260 if (strncmp(key
, "$", 1) != 0)
264 snprintf(fullname
, 256, "%s%s", name
, key
+ 1);
265 state
= alloc_tag_state(tag
);
266 set_state(my_id
, fullname
, sym
, state
);
269 static int get_array_mtag_offset(struct expression
*expr
, mtag_t
*tag
, int *offset
)
271 struct expression
*array
, *offset_expr
;
278 array
= get_array_base(expr
);
281 type
= get_type(array
);
282 if (!type
|| type
->type
!= SYM_ARRAY
)
284 type
= get_real_base_type(type
);
285 if (!type_bytes(type
))
288 if (!get_mtag(array
, tag
))
291 offset_expr
= get_array_offset(expr
);
292 if (!get_value(offset_expr
, &sval
))
294 *offset
= sval
.value
* type_bytes(type
);
299 static int get_implied_mtag_offset(struct expression
*expr
, mtag_t
*tag
, int *offset
)
301 struct smatch_state
*state
;
305 type
= get_type(expr
);
306 if (!type_is_ptr(type
))
308 state
= get_extra_state(expr
);
309 if (!state
|| !estate_get_single_value(state
, &sval
) || sval
.value
== 0)
312 *tag
= sval
.uvalue
& ~MTAG_OFFSET_MASK
;
313 *offset
= sval
.uvalue
& MTAG_OFFSET_MASK
;
317 static int get_mtag_cnt
;
318 int get_mtag(struct expression
*expr
, mtag_t
*tag
)
320 struct smatch_state
*state
;
323 expr
= strip_expr(expr
);
327 if (get_mtag_cnt
> 0)
332 switch (expr
->type
) {
334 if (get_string_mtag(expr
, tag
)) {
340 if (get_toplevel_mtag(expr
->symbol
, tag
)) {
346 if (get_deref_mtag(expr
, tag
)) {
353 state
= get_state_expr(my_id
, expr
);
357 *tag
= *(mtag_t
*)state
->data
;
367 int get_mtag_offset(struct expression
*expr
, mtag_t
*tag
, int *offset
)
373 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
374 return get_mtag_offset(expr
->unop
, tag
, offset
);
375 if (get_implied_mtag_offset(expr
, tag
, offset
))
377 if (!get_mtag(expr
, tag
))
379 expr
= strip_expr(expr
);
380 if (expr
->type
== EXPR_SYMBOL
) {
384 val
= get_member_offset_from_deref(expr
);
391 int create_mtag_alias(mtag_t tag
, struct expression
*expr
, mtag_t
*new)
394 int lines_from_start
;
398 * We need the alias to be unique. It's not totally required that it
399 * be the same from one DB build to then next, but it makes debugging
407 lines_from_start
= expr
->pos
.line
- cur_func_sym
->pos
.line
;
408 str
= expr_to_str(expr
);
409 snprintf(buf
, sizeof(buf
), "%lld %d %s", tag
, lines_from_start
, str
);
412 *new = str_to_tag(buf
);
413 sql_insert_mtag_alias(tag
, *new);
418 int expr_to_mtag_offset(struct expression
*expr
, mtag_t
*tag
, int *offset
)
422 expr
= strip_expr(expr
);
427 return get_array_mtag_offset(expr
, tag
, offset
);
429 if (expr
->type
== EXPR_DEREF
) {
430 *offset
= get_member_offset_from_deref(expr
);
433 return get_mtag(expr
->deref
, tag
);
436 if (get_implied_mtag_offset(expr
, tag
, offset
))
439 return get_mtag(expr
, tag
);
442 int get_mtag_sval(struct expression
*expr
, sval_t
*sval
)
448 if (bits_in_pointer
!= 64)
451 expr
= strip_expr(expr
);
453 type
= get_type(expr
);
454 if (!type_is_ptr(type
))
457 * There are only three options:
459 * 1) An array address:
461 * 2) An address like so:
462 * p = &my_struct->member;
468 if (expr
->type
== EXPR_STRING
&& get_string_mtag(expr
, &tag
))
471 if (type
->type
== SYM_ARRAY
&& get_toplevel_mtag(expr
->symbol
, &tag
))
474 if (get_implied_mtag_offset(expr
, &tag
, &offset
))
477 if (expr
->type
!= EXPR_PREOP
|| expr
->op
!= '&')
479 expr
= strip_expr(expr
->unop
);
481 if (!expr_to_mtag_offset(expr
, &tag
, &offset
))
483 if (offset
> MTAG_OFFSET_MASK
)
484 offset
= MTAG_OFFSET_MASK
;
488 sval
->uvalue
= tag
| offset
;
493 static struct expression
*remove_dereference(struct expression
*expr
)
495 expr
= strip_expr(expr
);
497 if (expr
->type
== EXPR_PREOP
&& expr
->op
== '*')
498 return strip_expr(expr
->unop
);
499 return preop_expression(expr
, '&');
502 int get_mtag_addr_sval(struct expression
*expr
, sval_t
*sval
)
504 return get_mtag_sval(remove_dereference(expr
), sval
);
507 static void print_stored_to_mtag(int return_id
, char *return_ranges
, struct expression
*expr
)
511 const char *param_name
;
514 FOR_EACH_MY_SM(my_id
, __get_cur_stree(), sm
) {
515 if (!sm
->state
->data
)
518 param
= get_param_num_from_sym(sm
->sym
);
521 param_name
= get_param_name(sm
);
524 if (strcmp(param_name
, "$") == 0)
527 snprintf(buf
, sizeof(buf
), "%lld", *(mtag_t
*)sm
->state
->data
);
528 sql_insert_return_states(return_id
, return_ranges
, MEMORY_TAG
, param
, param_name
, buf
);
529 } END_FOR_EACH_SM(sm
);
532 void register_mtag(int id
)
538 * The mtag stuff only works on 64 systems because we store the
539 * information in the pointer itself.
540 * bit 63 : set for alias mtags
541 * bit 62-12: mtag hash
545 if (bits_in_pointer
!= 64)
548 add_hook(&global_variable
, BASE_HOOK
);
550 add_function_assign_hook("kmalloc", &alloc_assign
, NULL
);
551 add_function_assign_hook("kzalloc", &alloc_assign
, NULL
);
553 select_return_states_hook(BUF_SIZE
, &db_returns_buf_size
);
555 add_hook(&match_call_info
, FUNCTION_CALL_HOOK
);
556 select_caller_info_hook(save_caller_info
, MEMORY_TAG
);
557 add_split_return_callback(&print_stored_to_mtag
);
558 select_return_states_hook(MEMORY_TAG
, db_returns_memory_tag
);