Print this page
12724 update smatch to 0.6.1-rc1-il-5
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/tools/smatch/src/smatch_mtag.c
+++ new/usr/src/tools/smatch/src/smatch_mtag.c
1 1 /*
2 2 * Copyright (C) 2017 Oracle. All rights reserved.
3 3 *
4 4 * This program is free software; you can redistribute it and/or
5 5 * modify it under the terms of the GNU General Public License
6 6 * as published by the Free Software Foundation; either version 2
7 7 * of the License, or (at your option) any later version.
8 8 *
9 9 * This program is distributed in the hope that it will be useful,
10 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 * GNU General Public License for more details.
13 13 *
14 14 * You should have received a copy of the GNU General Public License
15 15 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
16 16 */
17 17
18 18 /*
19 19 * One problem that I have is that it's really hard to track how pointers are
20 20 * passed around. For example, it would be nice to know that the probe() and
21 21 * remove() functions get the same pci_dev pointer. It would be good to know
22 22 * what pointers we're passing to the open() and close() functions. But that
23 23 * information gets lost in a call tree full of function pointer calls.
24 24 *
25 25 * I think the first step is to start naming specific pointers. So when a
26 26 * pointer is allocated, then it gets a tag. So calls to kmalloc() generate a
27 27 * tag. But we might not use that, because there might be a better name like
28 28 * framebuffer_alloc(). The framebuffer_alloc() is interesting because there is
29 29 * one per driver and it's passed around to all the file operations.
30 30 *
31 31 * Perhaps we could make a list of functions like framebuffer_alloc() which take
32 32 * a size and say that those are the interesting alloc functions.
33 33 *
34 34 * Another place where we would maybe name the pointer is when they are passed
35 35 * to the probe(). Because that's an important pointer, since there is one
36 36 * per driver (sort of).
37 37 *
38 38 * My vision is that you could take a pointer and trace it back to a global. So
39 39 * I'm going to track that pointer_tag - 28 bytes takes you to another pointer
40 40 * tag. You could follow that one back and so on. Also when we pass a pointer
41 41 * to a function that would be recorded as sort of a link or path or something.
42 42 *
43 43 */
44 44
45 45 #include "smatch.h"
46 46 #include "smatch_slist.h"
47 47 #include "smatch_extra.h"
48 48
49 49 #include <openssl/md5.h>
50 50
51 51 static int my_id;
52 52
53 53 mtag_t str_to_mtag(const char *str)
54 54 {
55 55 unsigned char c[MD5_DIGEST_LENGTH];
56 56 unsigned long long *tag = (unsigned long long *)&c;
57 57 MD5_CTX mdContext;
58 58 int len;
59 59
60 60 len = strlen(str);
↓ open down ↓ |
60 lines elided |
↑ open up ↑ |
61 61 MD5_Init(&mdContext);
62 62 MD5_Update(&mdContext, str, len);
63 63 MD5_Final(c, &mdContext);
64 64
65 65 *tag &= ~MTAG_ALIAS_BIT;
66 66 *tag &= ~MTAG_OFFSET_MASK;
67 67
68 68 return *tag;
69 69 }
70 70
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},
79 -};
71 +static int save_allocator(void *_allocator, int argc, char **argv, char **azColName)
72 +{
73 + char **allocator = _allocator;
80 74
81 -static bool is_mtag_call(struct expression *expr)
75 + if (*allocator) {
76 + if (strcmp(*allocator, argv[0]) == 0)
77 + return 0;
78 + /* should be impossible */
79 + free_string(*allocator);
80 + *allocator = alloc_string("unknown");
81 + return 0;
82 + }
83 + *allocator = alloc_string(argv[0]);
84 + return 0;
85 +}
86 +
87 +char *get_allocator_info_from_tag(mtag_t tag)
82 88 {
83 - struct expression *arg;
84 - int i;
89 + char *allocator = NULL;
90 +
91 + run_sql(save_allocator, &allocator,
92 + "select value from mtag_info where tag = %lld and type = %d;",
93 + tag, ALLOCATOR);
94 +
95 + return allocator;
96 +}
97 +
98 +static char *get_allocator_info(struct expression *expr, struct smatch_state *state)
99 +{
85 100 sval_t sval;
86 101
102 + if (expr->type != EXPR_ASSIGNMENT)
103 + return NULL;
104 + if (estate_get_single_value(state, &sval))
105 + return get_allocator_info_from_tag(sval.value);
106 +
107 + expr = strip_expr(expr->right);
87 108 if (expr->type != EXPR_CALL ||
88 - expr->fn->type != EXPR_SYMBOL ||
89 - !expr->fn->symbol)
90 - return false;
109 + !expr->fn ||
110 + expr->fn->type != EXPR_SYMBOL)
111 + return NULL;
112 + return expr_to_str(expr->fn);
113 +}
91 114
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;
95 - }
96 - if (i == ARRAY_SIZE(allocator_info))
97 - return false;
115 +static void update_mtag_info(struct expression *expr, mtag_t tag,
116 + const char *left_name, const char *tag_info,
117 + struct smatch_state *state)
118 +{
119 + char *allocator;
98 120
99 - arg = get_argument_from_call_expr(expr->args, allocator_info[i].size_arg);
100 - if (!get_implied_value(arg, &sval))
101 - return false;
121 + sql_insert_mtag_about(tag, left_name, tag_info);
102 122
103 - return true;
123 + allocator = get_allocator_info(expr, state);
124 + if (allocator)
125 + sql_insert_mtag_info(tag, ALLOCATOR, allocator);
104 126 }
105 127
106 -struct smatch_state *swap_mtag_return(struct expression *expr, struct smatch_state *state)
128 +struct smatch_state *get_mtag_return(struct expression *expr, struct smatch_state *state)
107 129 {
108 130 struct expression *left, *right;
109 131 char *left_name, *right_name;
110 132 struct symbol *left_sym;
111 133 struct range_list *rl;
112 134 char buf[256];
113 135 mtag_t tag;
114 136 sval_t tag_sval;
115 137
116 138 if (!expr || expr->type != EXPR_ASSIGNMENT || expr->op != '=')
117 - return state;
139 + return NULL;
140 + if (!is_fresh_alloc(expr->right))
141 + return NULL;
142 + if (!rl_intersection(estate_rl(state), valid_ptr_rl))
143 + return NULL;
118 144
119 - if (!estate_rl(state) || strcmp(state->name, "0,4096-ptr_max") != 0)
120 - return state;
121 -
122 145 left = strip_expr(expr->left);
123 146 right = strip_expr(expr->right);
124 147
125 - if (!is_mtag_call(right))
126 - return state;
127 -
128 148 left_name = expr_to_str_sym(left, &left_sym);
129 149 if (!left_name || !left_sym)
130 - return state;
150 + return NULL;
131 151 right_name = expr_to_str(right);
132 152
133 153 snprintf(buf, sizeof(buf), "%s %s %s %s", get_filename(), get_function(),
134 154 left_name, right_name);
135 155 tag = str_to_mtag(buf);
136 156 tag_sval.type = estate_type(state);
137 157 tag_sval.uvalue = tag;
138 158
139 159 rl = rl_filter(estate_rl(state), valid_ptr_rl);
140 160 rl = clone_rl(rl);
141 161 add_range(&rl, tag_sval, tag_sval);
142 162
143 - sql_insert_mtag_about(tag, left_name, buf);
163 + update_mtag_info(expr, tag, left_name, buf, state);
144 164
145 165 free_string(left_name);
146 166 free_string(right_name);
147 167
148 168 return alloc_estate_rl(rl);
149 169 }
150 170
151 171 int get_string_mtag(struct expression *expr, mtag_t *tag)
152 172 {
153 173 mtag_t xor;
154 174
155 175 if (expr->type != EXPR_STRING || !expr->string)
156 176 return 0;
157 177
158 178 /* I was worried about collisions so I added a xor */
159 179 xor = str_to_mtag("__smatch string");
160 180 *tag = str_to_mtag(expr->string->data);
161 181 *tag = *tag ^ xor;
162 182
163 183 return 1;
164 184 }
165 185
166 186 int get_toplevel_mtag(struct symbol *sym, mtag_t *tag)
167 187 {
168 188 char buf[256];
169 189
170 190 if (!sym)
171 191 return 0;
172 192
173 193 if (!sym->ident ||
174 194 !(sym->ctype.modifiers & MOD_TOPLEVEL))
175 195 return 0;
176 196
177 197 snprintf(buf, sizeof(buf), "%s %s",
178 198 (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern",
179 199 sym->ident->name);
180 200 *tag = str_to_mtag(buf);
181 201 return 1;
182 202 }
183 203
184 204 bool get_symbol_mtag(struct symbol *sym, mtag_t *tag)
185 205 {
186 206 char buf[256];
187 207
188 208 if (!sym || !sym->ident)
189 209 return false;
190 210
191 211 if (get_toplevel_mtag(sym, tag))
192 212 return true;
193 213
194 214 if (get_param_num_from_sym(sym) >= 0)
195 215 return false;
196 216
197 217 snprintf(buf, sizeof(buf), "%s %s %s",
198 218 get_filename(), get_function(), sym->ident->name);
199 219 *tag = str_to_mtag(buf);
200 220 return true;
201 221 }
202 222
203 223 static void global_variable(struct symbol *sym)
204 224 {
205 225 mtag_t tag;
206 226
207 227 if (!get_toplevel_mtag(sym, &tag))
208 228 return;
209 229
210 230 sql_insert_mtag_about(tag,
211 231 sym->ident->name,
212 232 (sym->ctype.modifiers & MOD_STATIC) ? get_filename() : "extern");
213 233 }
214 234
215 235 static int get_array_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
216 236 {
217 237 struct expression *array, *offset_expr;
218 238 struct symbol *type;
219 239 sval_t sval;
220 240 int start_offset;
221 241
222 242 if (!is_array(expr))
223 243 return 0;
224 244
225 245 array = get_array_base(expr);
226 246 if (!array)
227 247 return 0;
228 248 type = get_type(array);
229 249 if (!type || type->type != SYM_ARRAY)
230 250 return 0;
231 251 type = get_real_base_type(type);
232 252 if (!type_bytes(type))
233 253 return 0;
234 254
235 255 if (!expr_to_mtag_offset(array, tag, &start_offset))
236 256 return 0;
237 257
238 258 offset_expr = get_array_offset(expr);
239 259 if (!get_value(offset_expr, &sval))
240 260 return 0;
241 261 *offset = start_offset + sval.value * type_bytes(type);
242 262
243 263 return 1;
244 264 }
245 265
246 266 struct range_list *swap_mtag_seed(struct expression *expr, struct range_list *rl)
247 267 {
248 268 char buf[256];
249 269 char *name;
250 270 sval_t sval;
251 271 mtag_t tag;
252 272
253 273 if (!rl_to_sval(rl, &sval))
254 274 return rl;
255 275 if (sval.type->type != SYM_PTR || sval.uvalue != MTAG_SEED)
256 276 return rl;
257 277
258 278 name = expr_to_str(expr);
259 279 snprintf(buf, sizeof(buf), "%s %s %s", get_filename(), get_function(), name);
260 280 free_string(name);
261 281 tag = str_to_mtag(buf);
262 282 sval.value = tag;
263 283 return alloc_rl(sval, sval);
264 284 }
265 285
266 286 int create_mtag_alias(mtag_t tag, struct expression *expr, mtag_t *new)
267 287 {
268 288 char buf[256];
269 289 int lines_from_start;
270 290 char *str;
271 291
272 292 /*
273 293 * We need the alias to be unique. It's not totally required that it
274 294 * be the same from one DB build to then next, but it makes debugging
275 295 * a bit simpler.
276 296 *
277 297 */
278 298
279 299 if (!cur_func_sym)
280 300 return 0;
281 301
282 302 lines_from_start = expr->pos.line - cur_func_sym->pos.line;
283 303 str = expr_to_str(expr);
284 304 snprintf(buf, sizeof(buf), "%lld %d %s", tag, lines_from_start, str);
285 305 free_string(str);
286 306
287 307 *new = str_to_mtag(buf);
288 308 sql_insert_mtag_alias(tag, *new);
289 309
290 310 return 1;
291 311 }
292 312
293 313 static int get_implied_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
294 314 {
295 315 struct smatch_state *state;
296 316 struct symbol *type;
297 317 sval_t sval;
298 318
299 319 type = get_type(expr);
300 320 if (!type_is_ptr(type))
301 321 return 0;
302 322 state = get_extra_state(expr);
303 323 if (!state || !estate_get_single_value(state, &sval) || sval.value == 0)
304 324 return 0;
305 325
306 326 *tag = sval.uvalue & ~MTAG_OFFSET_MASK;
307 327 *offset = sval.uvalue & MTAG_OFFSET_MASK;
308 328 return 1;
309 329 }
310 330
311 331 /*
312 332 * The point of this function is to give you the mtag and the offset so
313 333 * you can look up the data in the DB. It takes an expression.
314 334 *
315 335 * So say you give it "foo->bar". Then it would give you the offset of "bar"
316 336 * and the implied value of "foo". Or if you lookup "*foo" then the offset is
317 337 * zero and we look up the implied value of "foo. But if the expression is
318 338 * foo, then if "foo" is a global variable, then we get the mtag and the offset
319 339 * is zero. If "foo" is a local variable, then there is nothing to look up in
320 340 * the mtag_data table because that's handled by smatch_extra.c to this returns
321 341 * false.
322 342 *
323 343 */
324 344 int expr_to_mtag_offset(struct expression *expr, mtag_t *tag, int *offset)
325 345 {
326 346 *tag = 0;
327 347 *offset = 0;
328 348
329 349 if (bits_in_pointer != 64)
330 350 return 0;
331 351
332 352 expr = strip_expr(expr);
333 353 if (!expr)
334 354 return 0;
335 355
336 356 if (is_array(expr))
337 357 return get_array_mtag_offset(expr, tag, offset);
338 358
339 359 if (expr->type == EXPR_PREOP && expr->op == '*') {
340 360 expr = strip_expr(expr->unop);
341 361 return get_implied_mtag_offset(expr, tag, offset);
342 362 } else if (expr->type == EXPR_DEREF) {
343 363 int tmp, tmp_offset = 0;
344 364
345 365 while (expr->type == EXPR_DEREF) {
346 366 tmp = get_member_offset_from_deref(expr);
347 367 if (tmp < 0)
348 368 return 0;
349 369 tmp_offset += tmp;
350 370 expr = strip_expr(expr->deref);
351 371 }
352 372 *offset = tmp_offset;
353 373 if (expr->type == EXPR_PREOP && expr->op == '*') {
354 374 expr = strip_expr(expr->unop);
355 375
356 376 if (get_implied_mtag_offset(expr, tag, &tmp_offset)) {
357 377 // FIXME: look it up recursively?
358 378 if (tmp_offset)
359 379 return 0;
360 380 return 1;
361 381 }
362 382 return 0;
363 383 } else if (expr->type == EXPR_SYMBOL) {
364 384 return get_symbol_mtag(expr->symbol, tag);
365 385 }
366 386 return 0;
367 387 } else if (expr->type == EXPR_SYMBOL) {
368 388 return get_symbol_mtag(expr->symbol, tag);
369 389 }
370 390 return 0;
371 391 }
372 392
373 393 /*
374 394 * This function takes an address and returns an sval. Let's take some
375 395 * example things you might pass to it:
376 396 * foo->bar:
377 397 * If we were only called from smatch_math, we wouldn't need to bother with
378 398 * this because it's already been looked up in smatch_extra.c but this is
379 399 * also called from other places so we have to check smatch_extra.c.
380 400 * &foo
381 401 * If "foo" is global return the mtag for "foo".
382 402 * &foo.bar
383 403 * If "foo" is global return the mtag for "foo" + the offset of ".bar".
384 404 * It also handles string literals.
385 405 *
386 406 */
387 407 int get_mtag_sval(struct expression *expr, sval_t *sval)
388 408 {
389 409 struct symbol *type;
390 410 mtag_t tag;
391 411 int offset = 0;
392 412
393 413 if (bits_in_pointer != 64)
394 414 return 0;
395 415
396 416 expr = strip_expr(expr);
397 417
398 418 type = get_type(expr);
399 419 if (!type_is_ptr(type))
400 420 return 0;
401 421 /*
402 422 * There are several options:
403 423 *
404 424 * If the expr is a string literal, that's an address/mtag.
405 425 * SYM_ARRAY and SYM_FN are mtags. There are "&foo" type addresses.
406 426 * And there are saved pointers "p = &foo;"
407 427 *
408 428 */
409 429
410 430 if (expr->type == EXPR_STRING && get_string_mtag(expr, &tag))
411 431 goto found;
412 432
413 433 if (expr->type == EXPR_SYMBOL &&
414 434 (type->type == SYM_ARRAY || type->type == SYM_FN) &&
415 435 get_toplevel_mtag(expr->symbol, &tag))
416 436 goto found;
417 437
418 438 if (expr->type == EXPR_PREOP && expr->op == '&') {
419 439 expr = strip_expr(expr->unop);
420 440 if (expr_to_mtag_offset(expr, &tag, &offset))
421 441 goto found;
422 442 return 0;
423 443 }
424 444
425 445 if (get_implied_mtag_offset(expr, &tag, &offset))
426 446 goto found;
427 447
428 448 return 0;
429 449 found:
430 450 if (offset >= MTAG_OFFSET_MASK)
431 451 return 0;
432 452
433 453 sval->type = type;
434 454 sval->uvalue = tag | offset;
435 455
436 456 return 1;
437 457 }
438 458
439 459 void register_mtag(int id)
440 460 {
441 461 my_id = id;
442 462
443 463
444 464 /*
445 465 * The mtag stuff only works on 64 systems because we store the
446 466 * information in the pointer itself.
447 467 * bit 63 : set for alias mtags
448 468 * bit 62-12: mtag hash
449 469 * bit 11-0 : offset
450 470 *
451 471 */
452 472
453 473 add_hook(&global_variable, BASE_HOOK);
454 474 }
↓ open down ↓ |
301 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX