symbol.h: let is_ptr_type() take NULL pointers
[smatch.git] / smatch_mtag.c
blobf39e66d7911d605bf002a52ae9bca6a2568b06a9
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 &= ~(1ULL << 63);
81 return *tag;
84 static void alloc_assign(const char *fn, struct expression *expr, void *unused)
86 struct expression *left, *right;
87 char *left_name, *right_name;
88 struct symbol *left_sym;
89 char buf[256];
90 mtag_t tag;
92 if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
93 return;
94 left = strip_expr(expr->left);
95 right = strip_expr(expr->right);
96 if (right->type != EXPR_CALL || right->fn->type != EXPR_SYMBOL)
97 return;
99 left_name = expr_to_str_sym(left, &left_sym);
100 right_name = expr_to_str(right);
102 snprintf(buf, sizeof(buf), "%s %s %s %s", get_filename(), get_function(),
103 left_name, right_name);
104 tag = str_to_tag(buf);
106 sql_insert_mtag_about(tag, left_name, right_name);
108 if (left_name && left_sym)
109 set_state(my_id, left_name, left_sym, alloc_tag_state(tag));
111 free_string(left_name);
112 free_string(right_name);
115 int get_toplevel_mtag(struct symbol *sym, mtag_t *tag)
117 char buf[256];
119 if (!sym)
120 return 0;
122 if (!sym->ident ||
123 !(sym->ctype.modifiers & MOD_TOPLEVEL))
124 return 0;
126 snprintf(buf, sizeof(buf), "%s %s",
127 (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern",
128 sym->ident->name);
129 *tag = str_to_tag(buf);
130 return 1;
133 int get_deref_mtag(struct expression *expr, mtag_t *tag)
135 mtag_t container_tag, member_tag;
136 int offset;
139 * I'm not totally sure what I'm doing...
141 * This is supposed to get something like "global_var->ptr", but I don't
142 * feel like it's complete at all.
146 if (!get_mtag(expr->unop, &container_tag))
147 return 0;
149 offset = get_member_offset_from_deref(expr);
150 if (offset < 0)
151 return 0;
153 if (!mtag_map_select_tag(container_tag, -offset, &member_tag))
154 return 0;
156 *tag = member_tag;
157 return 1;
160 static void global_variable(struct symbol *sym)
162 mtag_t tag;
164 if (!get_toplevel_mtag(sym, &tag))
165 return;
167 sql_insert_mtag_about(tag,
168 sym->ident->name,
169 (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern");
172 static void db_returns_buf_size(struct expression *expr, int param, char *unused, char *math)
174 struct expression *call;
175 struct range_list *rl;
177 if (expr->type != EXPR_ASSIGNMENT)
178 return;
179 call = strip_expr(expr->right);
181 if (!parse_call_math_rl(call, math, &rl))
182 return;
183 // rl = cast_rl(&int_ctype, rl);
184 // set_state_expr(my_size_id, expr->left, alloc_estate_rl(rl));
187 static void match_call_info(struct expression *expr)
189 struct smatch_state *state;
190 struct expression *arg;
191 int i = -1;
193 FOR_EACH_PTR(expr->args, arg) {
194 i++;
195 state = get_state_expr(my_id, arg);
196 if (!state || !state->data)
197 continue;
198 sql_insert_caller_info(expr, MEMORY_TAG, i, "$", state->name);
199 } END_FOR_EACH_PTR(arg);
202 static void save_caller_info(const char *name, struct symbol *sym, char *key, char *value)
204 struct smatch_state *state;
205 char fullname[256];
206 mtag_t tag;
208 if (strncmp(key, "$", 1) != 0)
209 return;
211 tag = atoll(value);
212 snprintf(fullname, 256, "%s%s", name, key + 1);
213 state = alloc_tag_state(tag);
214 set_state(my_id, fullname, sym, state);
217 static int get_array_mtag(struct expression *expr, mtag_t *tag)
219 struct expression *base, *offset;
220 sval_t sval;
221 mtag_t base_tag;
222 char buf[256];
224 if (!is_array(expr))
225 return 0;
227 base = get_array_base(expr);
228 if (!base)
229 return 0;
230 offset = get_array_offset(expr);
231 if (!offset)
232 return 0;
234 if (!get_implied_value(offset, &sval))
235 return 0;
237 if (!get_mtag(base, &base_tag))
238 return 0;
240 snprintf(buf, sizeof(buf), "%lld + %lld", base_tag, sval.value);
241 *tag = str_to_tag(buf);
243 return 1;
246 static int get_mtag_cnt;
247 int get_mtag(struct expression *expr, mtag_t *tag)
249 struct smatch_state *state;
250 int ret = 0;
252 expr = strip_expr(expr);
253 if (!expr)
254 return 0;
256 if (get_mtag_cnt > 0)
257 return 0;
259 get_mtag_cnt++;
261 /* FIXME: This doesn't feel like the right thing at all */
262 if (expr->type == EXPR_PREOP) {
263 if (expr->op == '&')
264 expr = strip_expr(expr->unop);
265 if (expr->op == '*')
266 expr = strip_expr(expr->unop);
269 switch (expr->type) {
270 case EXPR_SYMBOL:
271 if (get_toplevel_mtag(expr->symbol, tag)) {
272 ret = 1;
273 goto dec_cnt;
275 break;
276 case EXPR_DEREF:
277 if (get_deref_mtag(expr, tag)) {
278 ret = 1;
279 goto dec_cnt;
281 break;
282 case EXPR_BINOP:
283 ret = get_array_mtag(expr, tag);
284 goto dec_cnt;
287 state = get_state_expr(my_id, expr);
288 if (!state)
289 goto dec_cnt;
290 if (state->data) {
291 *tag = *(mtag_t *)state->data;
292 ret = 1;
293 goto dec_cnt;
296 dec_cnt:
297 get_mtag_cnt--;
298 return ret;
301 int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new)
303 char buf[256];
304 int lines_from_start;
305 char *str;
308 * We need the alias to be unique. It's not totally required that it
309 * be the same from one DB build to then next, but it makes debugging
310 * a bit simpler.
314 if (!cur_func_sym)
315 return 0;
317 lines_from_start = expr->pos.line - cur_func_sym->pos.line;
318 str = expr_to_str(expr);
319 snprintf(buf, sizeof(buf), "%lld %d %s", tag, lines_from_start, str);
320 free_string(str);
322 *new = str_to_tag(buf);
324 return 1;
327 int expr_to_mtag_name_offset(struct expression *expr, mtag_t *tag, char **name, int *offset)
329 static char buf[256];
331 *offset = 0;
332 *name = 0;
334 expr = strip_expr(expr);
335 if (!expr)
336 return 0;
338 if (expr->type == EXPR_DEREF) {
339 *offset = get_member_offset_from_deref(expr);
340 if (*offset < 0)
341 return 0;
342 snprintf(buf, sizeof(buf), "$->%s", expr->member->name);
343 *name = buf;
344 return get_mtag(expr->deref, tag);
347 snprintf(buf, sizeof(buf), "*$");
348 *name = buf;
350 return get_mtag(expr, tag);
353 void register_mtag(int id)
355 my_id = id;
357 add_hook(&global_variable, BASE_HOOK);
359 add_function_assign_hook("kmalloc", &alloc_assign, NULL);
360 add_function_assign_hook("kzalloc", &alloc_assign, NULL);
362 select_return_states_hook(BUF_SIZE, &db_returns_buf_size);
364 add_hook(&match_call_info, FUNCTION_CALL_HOOK);
365 select_caller_info_hook(save_caller_info, MEMORY_TAG);