scope: fix is_outer_stmt()
[smatch.git] / smatch_mtag.c
blob031b7ce7688be453581c689d4b32848f68faa51b
1 /*
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.
45 #include "smatch.h"
46 #include "smatch_slist.h"
47 #include "smatch_extra.h"
49 #include <openssl/md5.h>
51 static int my_id;
53 static struct smatch_state *alloc_tag_state(mtag_t tag)
55 struct smatch_state *state;
56 char buf[64];
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;
64 return state;
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;
71 MD5_CTX mdContext;
72 int len;
74 len = strlen(str);
75 MD5_Init(&mdContext);
76 MD5_Update(&mdContext, str, len);
77 MD5_Final(c, &mdContext);
79 *tag &= ~MTAG_ALIAS_BIT;
80 *tag &= ~MTAG_OFFSET_MASK;
82 return *tag;
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;
90 char buf[256];
91 mtag_t tag;
93 if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
94 return;
95 left = strip_expr(expr->left);
96 right = strip_expr(expr->right);
97 if (right->type != EXPR_CALL || right->fn->type != EXPR_SYMBOL)
98 return;
100 left_name = expr_to_str_sym(left, &left_sym);
101 right_name = expr_to_str(right);
103 snprintf(buf, sizeof(buf), "%s %s %s %s", get_filename(), get_function(),
104 left_name, right_name);
105 tag = str_to_tag(buf);
107 sql_insert_mtag_about(tag, left_name, right_name);
109 if (left_name && left_sym)
110 set_state(my_id, left_name, left_sym, alloc_tag_state(tag));
112 free_string(left_name);
113 free_string(right_name);
116 int get_string_mtag(struct expression *expr, mtag_t *tag)
118 mtag_t xor;
120 if (expr->type != EXPR_STRING || !expr->string)
121 return 0;
123 /* I was worried about collisions so I added a xor */
124 xor = str_to_tag("__smatch string");
125 *tag = str_to_tag(expr->string->data);
126 *tag = *tag ^ xor;
128 return 1;
131 int get_toplevel_mtag(struct symbol *sym, mtag_t *tag)
133 char buf[256];
135 if (!sym)
136 return 0;
138 if (!sym->ident ||
139 !(sym->ctype.modifiers & MOD_TOPLEVEL))
140 return 0;
142 snprintf(buf, sizeof(buf), "%s %s",
143 (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern",
144 sym->ident->name);
145 *tag = str_to_tag(buf);
146 return 1;
149 int get_deref_mtag(struct expression *expr, mtag_t *tag)
151 mtag_t container_tag, member_tag;
152 int offset;
155 * I'm not totally sure what I'm doing...
157 * This is supposed to get something like "global_var->ptr", but I don't
158 * feel like it's complete at all.
162 if (!get_mtag(expr->unop, &container_tag))
163 return 0;
165 offset = get_member_offset_from_deref(expr);
166 if (offset < 0)
167 return 0;
169 if (!mtag_map_select_tag(container_tag, -offset, &member_tag))
170 return 0;
172 *tag = member_tag;
173 return 1;
176 static void global_variable(struct symbol *sym)
178 mtag_t tag;
180 if (!get_toplevel_mtag(sym, &tag))
181 return;
183 sql_insert_mtag_about(tag,
184 sym->ident->name,
185 (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern");
188 static void db_returns_buf_size(struct expression *expr, int param, char *unused, char *math)
190 struct expression *call;
191 struct range_list *rl;
193 if (expr->type != EXPR_ASSIGNMENT)
194 return;
195 call = strip_expr(expr->right);
197 if (!parse_call_math_rl(call, math, &rl))
198 return;
199 // rl = cast_rl(&int_ctype, rl);
200 // set_state_expr(my_size_id, expr->left, alloc_estate_rl(rl));
203 static void db_returns_memory_tag(struct expression *expr, int param, char *key, char *value)
205 struct expression *call, *arg;
206 mtag_t tag, alias;
207 char *name;
208 struct symbol *sym;
210 call = strip_expr(expr);
211 while (call->type == EXPR_ASSIGNMENT)
212 call = strip_expr(call->right);
213 if (call->type != EXPR_CALL)
214 return;
216 tag = strtoul(value, NULL, 10);
218 if (!create_mtag_alias(tag, call, &alias))
219 return;
221 arg = get_argument_from_call_expr(call->args, param);
222 if (!arg)
223 return;
225 name = get_variable_from_key(arg, key, &sym);
226 if (!name || !sym)
227 goto free;
229 set_state(my_id, name, sym, alloc_tag_state(alias));
230 free:
231 free_string(name);
234 static void match_call_info(struct expression *expr)
236 struct smatch_state *state;
237 struct expression *arg;
238 int i = -1;
240 FOR_EACH_PTR(expr->args, arg) {
241 i++;
242 state = get_state_expr(my_id, arg);
243 if (!state || !state->data)
244 continue;
245 sql_insert_caller_info(expr, MEMORY_TAG, i, "$", state->name);
246 } END_FOR_EACH_PTR(arg);
249 static void save_caller_info(const char *name, struct symbol *sym, char *key, char *value)
251 struct smatch_state *state;
252 char fullname[256];
253 mtag_t tag;
255 if (strncmp(key, "$", 1) != 0)
256 return;
258 tag = atoll(value);
259 snprintf(fullname, 256, "%s%s", name, key + 1);
260 state = alloc_tag_state(tag);
261 set_state(my_id, fullname, sym, state);
264 static int get_array_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
266 struct expression *array, *offset_expr;
267 struct symbol *type;
268 sval_t sval;
270 if (!is_array(expr))
271 return 0;
273 array = get_array_base(expr);
274 if (!array)
275 return 0;
276 type = get_type(array);
277 if (!type || type->type != SYM_ARRAY)
278 return 0;
279 type = get_real_base_type(type);
280 if (!type_bytes(type))
281 return 0;
283 if (!get_mtag(array, tag))
284 return 0;
286 offset_expr = get_array_offset(expr);
287 if (!get_value(offset_expr, &sval))
288 return 0;
289 *offset = sval.value * type_bytes(type);
291 return 1;
294 static int get_implied_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
296 struct smatch_state *state;
297 struct symbol *type;
298 sval_t sval;
300 type = get_type(expr);
301 if (!type_is_ptr(type))
302 return 0;
303 state = get_extra_state(expr);
304 if (!state || !estate_get_single_value(state, &sval) || sval.value == 0)
305 return 0;
307 *tag = sval.uvalue & ~MTAG_OFFSET_MASK;
308 *offset = sval.uvalue & MTAG_OFFSET_MASK;
309 return 1;
312 static int get_mtag_cnt;
313 int get_mtag(struct expression *expr, mtag_t *tag)
315 struct smatch_state *state;
316 int ret = 0;
318 expr = strip_expr(expr);
319 if (!expr)
320 return 0;
322 if (get_mtag_cnt > 0)
323 return 0;
325 get_mtag_cnt++;
327 switch (expr->type) {
328 case EXPR_STRING:
329 if (get_string_mtag(expr, tag)) {
330 ret = 1;
331 goto dec_cnt;
333 break;
334 case EXPR_SYMBOL:
335 if (get_toplevel_mtag(expr->symbol, tag)) {
336 ret = 1;
337 goto dec_cnt;
339 break;
340 case EXPR_DEREF:
341 if (get_deref_mtag(expr, tag)) {
342 ret = 1;
343 goto dec_cnt;
345 break;
348 state = get_state_expr(my_id, expr);
349 if (!state)
350 goto dec_cnt;
351 if (state->data) {
352 *tag = *(mtag_t *)state->data;
353 ret = 1;
354 goto dec_cnt;
357 dec_cnt:
358 get_mtag_cnt--;
359 return ret;
362 int get_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
364 int val;
366 if (!expr)
367 return 0;
368 if (expr->type == EXPR_PREOP && expr->op == '*')
369 return get_mtag_offset(expr->unop, tag, offset);
370 if (get_implied_mtag_offset(expr, tag, offset))
371 return 1;
372 if (!get_mtag(expr, tag))
373 return 0;
374 expr = strip_expr(expr);
375 if (expr->type == EXPR_SYMBOL) {
376 *offset = 0;
377 return 1;
379 val = get_member_offset_from_deref(expr);
380 if (val < 0)
381 return 0;
382 *offset = val;
383 return 1;
386 int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new)
388 char buf[256];
389 int lines_from_start;
390 char *str;
393 * We need the alias to be unique. It's not totally required that it
394 * be the same from one DB build to then next, but it makes debugging
395 * a bit simpler.
399 if (!cur_func_sym)
400 return 0;
402 lines_from_start = expr->pos.line - cur_func_sym->pos.line;
403 str = expr_to_str(expr);
404 snprintf(buf, sizeof(buf), "%lld %d %s", tag, lines_from_start, str);
405 free_string(str);
407 *new = str_to_tag(buf);
408 sql_insert_mtag_alias(tag, *new);
410 return 1;
413 int expr_to_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
415 *offset = 0;
417 expr = strip_expr(expr);
418 if (!expr)
419 return 0;
421 if (is_array(expr))
422 return get_array_mtag_offset(expr, tag, offset);
424 if (expr->type == EXPR_DEREF) {
425 *offset = get_member_offset_from_deref(expr);
426 if (*offset < 0)
427 return 0;
428 return get_mtag(expr->deref, tag);
431 if (get_implied_mtag_offset(expr, tag, offset))
432 return 1;
434 return get_mtag(expr, tag);
437 int get_mtag_sval(struct expression *expr, sval_t *sval)
439 struct symbol *type;
440 mtag_t tag;
441 int offset = 0;
443 if (bits_in_pointer != 64)
444 return 0;
446 expr = strip_expr(expr);
448 type = get_type(expr);
449 if (!type_is_ptr(type))
450 return 0;
452 * There are only three options:
454 * 1) An array address:
455 * p = array;
456 * 2) An address like so:
457 * p = &my_struct->member;
458 * 3) A pointer:
459 * p = pointer;
463 if (expr->type == EXPR_STRING && get_string_mtag(expr, &tag))
464 goto found;
466 if (type->type == SYM_ARRAY && get_toplevel_mtag(expr->symbol, &tag))
467 goto found;
469 if (get_implied_mtag_offset(expr, &tag, &offset))
470 goto found;
472 if (expr->type != EXPR_PREOP || expr->op != '&')
473 return 0;
474 expr = strip_expr(expr->unop);
476 if (!expr_to_mtag_offset(expr, &tag, &offset))
477 return 0;
478 if (offset > MTAG_OFFSET_MASK)
479 offset = MTAG_OFFSET_MASK;
481 found:
482 sval->type = type;
483 sval->uvalue = tag | offset;
485 return 1;
488 static struct expression *remove_dereference(struct expression *expr)
490 expr = strip_expr(expr);
492 if (expr->type == EXPR_PREOP && expr->op == '*')
493 return strip_expr(expr->unop);
494 return preop_expression(expr, '&');
497 int get_mtag_addr_sval(struct expression *expr, sval_t *sval)
499 return get_mtag_sval(remove_dereference(expr), sval);
502 static void print_stored_to_mtag(int return_id, char *return_ranges, struct expression *expr)
504 struct sm_state *sm;
505 char buf[256];
506 const char *param_name;
507 int param;
509 FOR_EACH_MY_SM(my_id, __get_cur_stree(), sm) {
510 if (!sm->state->data)
511 continue;
513 param = get_param_num_from_sym(sm->sym);
514 if (param < 0)
515 continue;
516 param_name = get_param_name(sm);
517 if (!param_name)
518 continue;
519 if (strcmp(param_name, "$") == 0)
520 continue;
522 snprintf(buf, sizeof(buf), "%lld", *(mtag_t *)sm->state->data);
523 sql_insert_return_states(return_id, return_ranges, MEMORY_TAG, param, param_name, buf);
524 } END_FOR_EACH_SM(sm);
527 void register_mtag(int id)
529 my_id = id;
533 * The mtag stuff only works on 64 systems because we store the
534 * information in the pointer itself.
535 * bit 63 : set for alias mtags
536 * bit 62-12: mtag hash
537 * bit 11-0 : offset
540 if (bits_in_pointer != 64)
541 return;
543 add_hook(&global_variable, BASE_HOOK);
545 add_function_assign_hook("kmalloc", &alloc_assign, NULL);
546 add_function_assign_hook("kzalloc", &alloc_assign, NULL);
548 select_return_states_hook(BUF_SIZE, &db_returns_buf_size);
550 add_hook(&match_call_info, FUNCTION_CALL_HOOK);
551 select_caller_info_hook(save_caller_info, MEMORY_TAG);
552 add_split_return_callback(&print_stored_to_mtag);
553 select_return_states_hook(MEMORY_TAG, db_returns_memory_tag);