mtag: tweak get_mtag_sval()
[smatch.git] / smatch_mtag.c
blob0d8bb21f5f4dc8891ae15a59ea177e7f41083e7c
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 mtag_t str_to_tag(const char *str)
55 unsigned char c[MD5_DIGEST_LENGTH];
56 unsigned long long *tag = (unsigned long long *)&c;
57 MD5_CTX mdContext;
58 int len;
60 len = strlen(str);
61 MD5_Init(&mdContext);
62 MD5_Update(&mdContext, str, len);
63 MD5_Final(c, &mdContext);
65 *tag &= ~MTAG_ALIAS_BIT;
66 *tag &= ~MTAG_OFFSET_MASK;
68 return *tag;
71 const struct {
72 const char *name;
73 int size_arg;
74 } allocator_info[] = {
75 { "kmalloc", 0 },
76 { "kzalloc", 0 },
77 { "devm_kmalloc", 1},
78 { "devm_kzalloc", 1},
81 static bool is_mtag_call(struct expression *expr)
83 struct expression *arg;
84 int i;
85 sval_t sval;
87 if (expr->type != EXPR_CALL ||
88 expr->fn->type != EXPR_SYMBOL ||
89 !expr->fn->symbol)
90 return false;
92 for (i = 0; i < ARRAY_SIZE(allocator_info); i++) {
93 if (strcmp(expr->fn->symbol->ident->name, allocator_info[i].name) == 0)
94 break;
96 if (i == ARRAY_SIZE(allocator_info))
97 return false;
99 arg = get_argument_from_call_expr(expr->args, allocator_info[i].size_arg);
100 if (!get_implied_value(arg, &sval))
101 return false;
103 return true;
106 struct smatch_state *swap_mtag_return(struct expression *expr, struct smatch_state *state)
108 struct expression *left, *right;
109 char *left_name, *right_name;
110 struct symbol *left_sym;
111 struct range_list *rl;
112 char buf[256];
113 mtag_t tag;
114 sval_t tag_sval;
116 if (!expr || expr->type != EXPR_ASSIGNMENT || expr->op != '=')
117 return state;
119 if (!estate_rl(state) || strcmp(state->name, "0,4096-ptr_max") != 0)
120 return state;
122 left = strip_expr(expr->left);
123 right = strip_expr(expr->right);
125 if (!is_mtag_call(right))
126 return state;
128 left_name = expr_to_str_sym(left, &left_sym);
129 if (!left_name || !left_sym)
130 return state;
131 right_name = expr_to_str(right);
133 snprintf(buf, sizeof(buf), "%s %s %s %s", get_filename(), get_function(),
134 left_name, right_name);
135 tag = str_to_tag(buf);
136 tag_sval.type = estate_type(state);
137 tag_sval.uvalue = tag;
139 rl = rl_filter(estate_rl(state), valid_ptr_rl);
140 rl = clone_rl(rl);
141 add_range(&rl, tag_sval, tag_sval);
143 sql_insert_mtag_about(tag, left_name, buf);
145 free_string(left_name);
146 free_string(right_name);
148 return alloc_estate_rl(rl);
151 int get_string_mtag(struct expression *expr, mtag_t *tag)
153 mtag_t xor;
155 if (expr->type != EXPR_STRING || !expr->string)
156 return 0;
158 /* I was worried about collisions so I added a xor */
159 xor = str_to_tag("__smatch string");
160 *tag = str_to_tag(expr->string->data);
161 *tag = *tag ^ xor;
163 return 1;
166 int get_toplevel_mtag(struct symbol *sym, mtag_t *tag)
168 char buf[256];
170 if (!sym)
171 return 0;
173 if (!sym->ident ||
174 !(sym->ctype.modifiers & MOD_TOPLEVEL))
175 return 0;
177 snprintf(buf, sizeof(buf), "%s %s",
178 (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern",
179 sym->ident->name);
180 *tag = str_to_tag(buf);
181 return 1;
184 static void global_variable(struct symbol *sym)
186 mtag_t tag;
188 if (!get_toplevel_mtag(sym, &tag))
189 return;
191 sql_insert_mtag_about(tag,
192 sym->ident->name,
193 (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern");
196 static int get_array_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
198 struct expression *array, *offset_expr;
199 struct symbol *type;
200 sval_t sval;
201 int start_offset;
203 if (!is_array(expr))
204 return 0;
206 array = get_array_base(expr);
207 if (!array)
208 return 0;
209 type = get_type(array);
210 if (!type || type->type != SYM_ARRAY)
211 return 0;
212 type = get_real_base_type(type);
213 if (!type_bytes(type))
214 return 0;
216 if (!expr_to_mtag_offset(array, tag, &start_offset))
217 return 0;
219 offset_expr = get_array_offset(expr);
220 if (!get_value(offset_expr, &sval))
221 return 0;
222 *offset = start_offset + sval.value * type_bytes(type);
224 return 1;
227 static int get_implied_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
229 struct smatch_state *state;
230 struct symbol *type;
231 sval_t sval;
233 type = get_type(expr);
234 if (!type_is_ptr(type))
235 return 0;
236 state = get_extra_state(expr);
237 if (!state || !estate_get_single_value(state, &sval) || sval.value == 0)
238 return 0;
240 *tag = sval.uvalue & ~MTAG_OFFSET_MASK;
241 *offset = sval.uvalue & MTAG_OFFSET_MASK;
242 return 1;
245 static int get_mtag_cnt;
246 int get_mtag(struct expression *expr, mtag_t *tag)
248 int ret = 0;
250 expr = strip_expr(expr);
251 if (!expr)
252 return 0;
254 if (get_mtag_cnt > 0)
255 return 0;
257 get_mtag_cnt++;
259 switch (expr->type) {
260 case EXPR_STRING:
261 if (get_string_mtag(expr, tag)) {
262 ret = 1;
263 goto dec_cnt;
265 break;
266 case EXPR_SYMBOL:
267 if (get_toplevel_mtag(expr->symbol, tag)) {
268 ret = 1;
269 goto dec_cnt;
271 break;
274 dec_cnt:
275 get_mtag_cnt--;
276 return ret;
279 struct range_list *swap_mtag_seed(struct expression *expr, struct range_list *rl)
281 char buf[256];
282 char *name;
283 sval_t sval;
284 mtag_t tag;
286 if (!rl_to_sval(rl, &sval))
287 return rl;
288 if (sval.type->type != SYM_PTR || sval.uvalue != MTAG_SEED)
289 return rl;
291 name = expr_to_str(expr);
292 snprintf(buf, sizeof(buf), "%s %s %s", get_filename(), get_function(), name);
293 free_string(name);
294 tag = str_to_tag(buf);
295 sval.value = tag;
296 return alloc_rl(sval, sval);
299 int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new)
301 char buf[256];
302 int lines_from_start;
303 char *str;
306 * We need the alias to be unique. It's not totally required that it
307 * be the same from one DB build to then next, but it makes debugging
308 * a bit simpler.
312 if (!cur_func_sym)
313 return 0;
315 lines_from_start = expr->pos.line - cur_func_sym->pos.line;
316 str = expr_to_str(expr);
317 snprintf(buf, sizeof(buf), "%lld %d %s", tag, lines_from_start, str);
318 free_string(str);
320 *new = str_to_tag(buf);
321 sql_insert_mtag_alias(tag, *new);
323 return 1;
327 * The point of this function is to give you the mtag and the offset so
328 * you can look up the data in the DB. It takes an expression.
330 * So say you give it "foo->bar". Then it would give you the offset of "bar"
331 * and the implied value of "foo". Or if you lookup "*foo" then the offset is
332 * zero and we look up the implied value of "foo. But if the expression is
333 * foo, then if "foo" is a global variable, then we get the mtag and the offset
334 * is zero. If "foo" is a local variable, then there is nothing to look up in
335 * the mtag_data table because that's handled by smatch_extra.c to this returns
336 * false.
339 int expr_to_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
341 *tag = 0;
342 *offset = 0;
344 expr = strip_expr(expr);
345 if (!expr)
346 return 0;
348 if (is_array(expr))
349 return get_array_mtag_offset(expr, tag, offset);
351 if (expr->type == EXPR_PREOP && expr->op == '*') {
352 expr = strip_expr(expr->unop);
353 if (get_implied_mtag_offset(expr, tag, offset))
354 return 1;
355 } else if (expr->type == EXPR_DEREF) {
356 int next_offset;
358 *offset = get_member_offset_from_deref(expr);
359 if (*offset < 0)
360 return 0;
361 expr = expr->deref;
362 if (expr->type == EXPR_PREOP && expr->op == '*')
363 expr = strip_expr(expr->unop);
365 if (get_implied_mtag_offset(expr, tag, &next_offset)) {
366 // FIXME: look it up recursively?
367 if (next_offset)
368 return 0;
369 return 1;
373 switch (expr->type) {
374 case EXPR_STRING:
375 return get_string_mtag(expr, tag);
376 case EXPR_SYMBOL:
377 return get_toplevel_mtag(expr->symbol, tag);
379 return 0;
383 * This function takes an address and returns an sval. Let's take some
384 * example things you might pass to it:
385 * foo->bar:
386 * If we were only called from smatch_math, we wouldn't need to bother with
387 * this because it's already been looked up in smatch_extra.c but this is
388 * also called from other places so we have to check smatch_extra.c.
389 * &foo
390 * If "foo" is global return the mtag for "foo".
391 * &foo.bar
392 * If "foo" is global return the mtag for "foo" + the offset of ".bar".
393 * It also handles string literals.
396 int get_mtag_sval(struct expression *expr, sval_t *sval)
398 struct symbol *type;
399 mtag_t tag;
400 int offset = 0;
402 if (bits_in_pointer != 64)
403 return 0;
405 expr = strip_expr(expr);
407 type = get_type(expr);
408 if (!type_is_ptr(type))
409 return 0;
411 * There are only three options:
413 * 1) An array address:
414 * p = array;
415 * 2) An address like so:
416 * p = &my_struct->member;
417 * 3) A pointer:
418 * p = pointer;
422 if (expr->type == EXPR_STRING && get_string_mtag(expr, &tag))
423 goto found;
425 if (expr->type == EXPR_SYMBOL &&
426 (type->type == SYM_ARRAY || type->type == SYM_FN) &&
427 get_toplevel_mtag(expr->symbol, &tag))
428 goto found;
430 if (get_implied_mtag_offset(expr, &tag, &offset))
431 goto found;
433 if (expr->type != EXPR_PREOP || expr->op != '&')
434 return 0;
435 expr = strip_expr(expr->unop);
437 if (!expr_to_mtag_offset(expr, &tag, &offset))
438 return 0;
439 found:
440 if (offset >= MTAG_OFFSET_MASK)
441 return 0;
443 sval->type = type;
444 sval->uvalue = tag | offset;
446 return 1;
449 void register_mtag(int id)
451 my_id = id;
455 * The mtag stuff only works on 64 systems because we store the
456 * information in the pointer itself.
457 * bit 63 : set for alias mtags
458 * bit 62-12: mtag hash
459 * bit 11-0 : offset
462 if (bits_in_pointer != 64)
463 return;
465 add_hook(&global_variable, BASE_HOOK);