Print this page
12257 resync smatch to 0.6.1-rc1-il-4
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/tools/smatch/src/smatch_container_of.c
+++ new/usr/src/tools/smatch/src/smatch_container_of.c
1 1 /*
2 2 * Copyright (C) 2017 Oracle.
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 #include "smatch.h"
19 19 #include "smatch_slist.h"
20 20 #include "smatch_extra.h"
21 21
22 22 static int my_id;
23 23 static int param_id;
24 24
25 25 int get_param_from_container_of(struct expression *expr)
26 26 {
27 27 struct expression *param_expr;
28 28 struct symbol *type;
29 29 sval_t sval;
30 30 int param;
31 31
32 32
33 33 type = get_type(expr);
34 34 if (!type || type->type != SYM_PTR)
35 35 return -1;
36 36
37 37 expr = strip_expr(expr);
38 38 if (expr->type != EXPR_BINOP || expr->op != '-')
39 39 return -1;
40 40
41 41 if (!get_value(expr->right, &sval))
42 42 return -1;
43 43 if (sval.value < 0 || sval.value > 4096)
44 44 return -1;
45 45
46 46 param_expr = get_assigned_expr(expr->left);
47 47 if (!param_expr)
48 48 return -1;
49 49 param = get_param_num(param_expr);
50 50 if (param < 0)
51 51 return -1;
52 52
53 53 return param;
54 54 }
55 55
56 56 int get_offset_from_container_of(struct expression *expr)
57 57 {
58 58 struct expression *param_expr;
59 59 struct symbol *type;
60 60 sval_t sval;
61 61
62 62 type = get_type(expr);
63 63 if (!type || type->type != SYM_PTR)
64 64 return -1;
65 65
66 66 expr = strip_expr(expr);
67 67 if (expr->type != EXPR_BINOP || expr->op != '-')
68 68 return -1;
69 69
70 70 if (!get_value(expr->right, &sval))
71 71 return -1;
72 72 if (sval.value < 0 || sval.value > 4096)
73 73 return -1;
74 74
75 75 param_expr = get_assigned_expr(expr->left);
76 76 if (!param_expr)
77 77 return -1;
78 78
79 79 return sval.value;
80 80 }
81 81
82 82 static void print_returns_container_of(int return_id, char *return_ranges, struct expression *expr)
83 83 {
84 84 int offset;
85 85 int param;
86 86 char key[64];
87 87 char value[64];
88 88
89 89 param = get_param_from_container_of(expr);
90 90 if (param < 0)
91 91 return;
92 92 offset = get_offset_from_container_of(expr);
93 93 if (offset < 0)
94 94 return;
95 95
96 96 snprintf(key, sizeof(key), "%d", param);
97 97 snprintf(value, sizeof(value), "-%d", offset);
98 98
99 99 /* no need to add it to return_implies because it's not really param_used */
100 100 sql_insert_return_states(return_id, return_ranges, CONTAINER, -1,
101 101 key, value);
102 102 }
103 103
104 104 static int get_deref_count(struct expression *expr)
105 105 {
106 106 int cnt = 0;
107 107
108 108 while (expr && expr->type == EXPR_DEREF) {
109 109 expr = expr->deref;
110 110 if (expr->type == EXPR_PREOP && expr->op == '*')
111 111 expr = expr->unop;
112 112 cnt++;
113 113 if (cnt > 100)
114 114 return -1;
115 115 }
116 116 return cnt;
117 117 }
118 118
119 119 static struct expression *get_partial_deref(struct expression *expr, int cnt)
120 120 {
121 121 while (--cnt >= 0) {
122 122 if (!expr || expr->type != EXPR_DEREF)
123 123 return expr;
124 124 expr = expr->deref;
125 125 if (expr->type == EXPR_PREOP && expr->op == '*')
126 126 expr = expr->unop;
127 127 }
128 128 return expr;
129 129 }
130 130
131 131 static int partial_deref_to_offset_str(struct expression *expr, int cnt, char op, char *buf, int size)
132 132 {
133 133 int n, offset;
134 134
135 135 if (cnt == 0)
136 136 return snprintf(buf, size, "%c0", op);
137 137
138 138 n = 0;
139 139 while (--cnt >= 0) {
140 140 offset = get_member_offset_from_deref(expr);
141 141 if (offset < 0)
142 142 return -1;
143 143 n += snprintf(buf + n, size - n, "%c%d", op, offset);
144 144 if (expr->type != EXPR_DEREF)
145 145 return -1;
146 146 expr = expr->deref;
147 147 if (expr->type == EXPR_PREOP && expr->op == '*')
148 148 expr = expr->unop;
149 149 }
150 150
151 151 return n;
152 152 }
153 153
154 154 static char *get_shared_str(struct expression *expr, struct expression *container)
155 155 {
156 156 struct expression *one, *two;
157 157 int exp, cont, min, ret, n;
158 158 static char buf[48];
159 159
160 160 exp = get_deref_count(expr);
161 161 cont = get_deref_count(container);
162 162 if (exp < 0 || cont < 0)
163 163 return NULL;
164 164
165 165 min = (exp < cont) ? exp : cont;
166 166 while (min >= 0) {
167 167 one = get_partial_deref(expr, exp - min);
168 168 two = get_partial_deref(container, cont - min);
169 169 if (expr_equiv(one, two))
170 170 goto found;
171 171 min--;
172 172 }
173 173
174 174 return NULL;
175 175
176 176 found:
177 177 ret = partial_deref_to_offset_str(expr, exp - min, '-', buf, sizeof(buf));
178 178 if (ret < 0)
179 179 return NULL;
180 180 n = ret;
181 181 ret = partial_deref_to_offset_str(container, cont - min, '+', buf + ret, sizeof(buf) - ret);
182 182 if (ret < 0)
183 183 return NULL;
184 184 n += ret;
185 185 if (n >= sizeof(buf))
186 186 return NULL;
187 187
188 188 return buf;
189 189 }
190 190
191 191 static char *get_stored_container_name(struct expression *container,
192 192 struct expression *expr)
193 193 {
194 194 struct smatch_state *state;
195 195 static char buf[64];
196 196 char *p;
197 197 int param;
198 198
199 199 if (!container || container->type != EXPR_SYMBOL)
200 200 return NULL;
201 201 if (!expr || expr->type != EXPR_SYMBOL)
202 202 return NULL;
203 203 state = get_state_expr(param_id, expr);
204 204 if (!state)
205 205 return NULL;
206 206
207 207 snprintf(buf, sizeof(buf), "%s", state->name);
↓ open down ↓ |
207 lines elided |
↑ open up ↑ |
208 208 p = strchr(buf, '|');
209 209 if (!p)
210 210 return NULL;
211 211 *p = '\0';
212 212 param = atoi(p + 2);
213 213 if (get_param_sym_from_num(param) == container->symbol)
214 214 return buf;
215 215 return NULL;
216 216 }
217 217
218 -char *get_container_name(struct expression *container, struct expression *expr)
218 +static char *get_container_name_helper(struct expression *container, struct expression *expr)
219 219 {
220 220 struct symbol *container_sym, *sym;
221 - struct expression *tmp;
222 221 static char buf[64];
223 222 char *ret, *shared;
224 223 bool star;
225 - int cnt;
226 224
227 225 expr = strip_expr(expr);
228 226 container = strip_expr(container);
227 + if (!expr || !container)
228 + return NULL;
229 229
230 230 ret = get_stored_container_name(container, expr);
231 231 if (ret)
232 232 return ret;
233 233
234 234 sym = expr_to_sym(expr);
235 235 container_sym = expr_to_sym(container);
236 - if (sym && sym == container_sym)
237 - goto found;
236 + if (!sym || !container_sym)
237 + return NULL;
238 + if (sym != container_sym)
239 + return NULL;
238 240
239 - cnt = 0;
240 - while ((tmp = get_assigned_expr(container))) {
241 - container = strip_expr(tmp);
242 - if (cnt++ > 3)
243 - break;
244 - }
245 -
246 - cnt = 0;
247 - while ((tmp = get_assigned_expr(expr))) {
248 - expr = strip_expr(tmp);
249 - if (cnt++ > 3)
250 - break;
251 - }
252 -
253 -found:
254 -
255 241 if (container->type == EXPR_DEREF)
256 242 star = true;
257 243 else
258 244 star = false;
259 245
260 246 if (container->type == EXPR_PREOP && container->op == '&')
261 247 container = strip_expr(container->unop);
262 248 if (expr->type == EXPR_PREOP && expr->op == '&')
263 249 expr = strip_expr(expr->unop);
264 250
265 - sym = expr_to_sym(expr);
266 - if (!sym)
267 - return NULL;
268 - container_sym = expr_to_sym(container);
269 - if (!container_sym || sym != container_sym)
270 - return NULL;
271 -
272 251 shared = get_shared_str(expr, container);
273 252 if (!shared)
274 253 return NULL;
275 254 if (star)
276 255 snprintf(buf, sizeof(buf), "*(%s)", shared);
277 256 else
278 257 snprintf(buf, sizeof(buf), "%s", shared);
279 258
280 259 return buf;
281 260 }
282 261
262 +char *get_container_name(struct expression *container, struct expression *expr)
263 +{
264 + char *ret;
265 +
266 + ret = get_container_name_helper(container, expr);
267 + if (ret)
268 + return ret;
269 +
270 + ret = get_container_name_helper(get_assigned_expr(container), expr);
271 + if (ret)
272 + return ret;
273 +
274 + ret = get_container_name_helper(container, get_assigned_expr(expr));
275 + if (ret)
276 + return ret;
277 +
278 + ret = get_container_name_helper(get_assigned_expr(container),
279 + get_assigned_expr(expr));
280 + if (ret)
281 + return ret;
282 +
283 + return NULL;
284 +}
285 +
283 286 static bool is_fn_ptr(struct expression *expr)
284 287 {
285 288 struct symbol *type;
286 289
287 290 if (!expr)
288 291 return false;
289 292 if (expr->type != EXPR_SYMBOL && expr->type != EXPR_DEREF)
290 293 return false;
291 294
292 295 type = get_type(expr);
293 296 if (!type || type->type != SYM_PTR)
294 297 return false;
295 298 type = get_real_base_type(type);
296 299 if (!type || type->type != SYM_FN)
297 300 return false;
298 301 return true;
299 302 }
300 303
301 304 static void match_call(struct expression *call)
302 305 {
303 306 struct expression *fn, *arg, *tmp;
304 307 bool found = false;
305 308 int fn_param, param;
306 309 char buf[32];
307 310 char *name;
308 311
309 312 /*
310 313 * We're trying to link the function with the parameter. There are a
311 314 * couple ways this can be passed:
312 315 * foo->func(foo, ...);
313 316 * foo->func(foo->x, ...);
314 317 * foo->bar.func(&foo->bar, ...);
315 318 * foo->bar->baz->func(foo, ...);
316 319 *
317 320 * So the method is basically to subtract the offsets until we get to
318 321 * the common bit, then add the member offsets to get the parameter.
319 322 *
320 323 * If we're taking an address then the offset math is not stared,
321 324 * otherwise it is. Starred means dereferenced.
322 325 */
323 326 fn = strip_expr(call->fn);
324 327
325 328 param = -1;
326 329 FOR_EACH_PTR(call->args, arg) {
327 330 param++;
328 331
329 332 name = get_container_name(arg, fn);
330 333 if (!name)
331 334 continue;
332 335
333 336 found = true;
334 337 sql_insert_caller_info(call, CONTAINER, param, name, "$(-1)");
335 338 } END_FOR_EACH_PTR(arg);
336 339
337 340 if (found)
338 341 return;
339 342
340 343 fn_param = -1;
341 344 FOR_EACH_PTR(call->args, arg) {
342 345 fn_param++;
343 346 if (!is_fn_ptr(arg))
344 347 continue;
345 348 param = -1;
346 349 FOR_EACH_PTR(call->args, tmp) {
347 350 param++;
348 351
349 352 /* the function isn't it's own container */
350 353 if (arg == tmp)
351 354 continue;
352 355
353 356 name = get_container_name(tmp, arg);
354 357 if (!name)
355 358 continue;
356 359
357 360 snprintf(buf, sizeof(buf), "$%d", param);
358 361 sql_insert_caller_info(call, CONTAINER, fn_param, name, buf);
359 362 return;
360 363 } END_FOR_EACH_PTR(tmp);
361 364 } END_FOR_EACH_PTR(arg);
362 365 }
363 366
364 367 static void db_passed_container(const char *name, struct symbol *sym, char *key, char *value)
365 368 {
366 369 char buf[64];
367 370
368 371 snprintf(buf, sizeof(buf), "%s|%s", key, value);
369 372 set_state(param_id, name, sym, alloc_state_str(buf));
370 373 }
371 374
372 375 struct db_info {
373 376 struct symbol *arg;
374 377 int prev_offset;
375 378 struct range_list *rl;
376 379 int star;
377 380 struct stree *stree;
378 381 };
379 382
380 383 static struct symbol *get_member_from_offset(struct symbol *sym, int offset)
381 384 {
382 385 struct symbol *type, *tmp;
383 386 int cur;
384 387
385 388 type = get_real_base_type(sym);
386 389 if (!type || type->type != SYM_PTR)
387 390 return NULL;
388 391 type = get_real_base_type(type);
389 392 if (!type || type->type != SYM_STRUCT)
390 393 return NULL;
391 394
392 395 cur = 0;
393 396 FOR_EACH_PTR(type->symbol_list, tmp) {
394 397 cur = ALIGN(cur, tmp->ctype.alignment);
395 398 if (offset == cur)
396 399 return tmp;
397 400 cur += type_bytes(tmp);
398 401 } END_FOR_EACH_PTR(tmp);
399 402 return NULL;
400 403 }
401 404
402 405 static struct symbol *get_member_type_from_offset(struct symbol *sym, int offset)
403 406 {
404 407 struct symbol *base_type;
405 408 struct symbol *member;
406 409
407 410 base_type = get_real_base_type(sym);
408 411 if (base_type && base_type->type == SYM_PTR)
409 412 base_type = get_real_base_type(base_type);
410 413 if (offset == 0 && base_type && base_type->type == SYM_BASETYPE)
411 414 return base_type;
412 415
413 416 member = get_member_from_offset(sym, offset);
414 417 if (!member)
415 418 return NULL;
416 419 return get_real_base_type(member);
417 420 }
418 421
419 422 static const char *get_name_from_offset(struct symbol *arg, int offset)
420 423 {
421 424 struct symbol *member, *type;
422 425 const char *name;
423 426 static char fullname[256];
424 427
425 428 name = arg->ident->name;
426 429
427 430 type = get_real_base_type(arg);
428 431 if (!type || type->type != SYM_PTR)
429 432 return name;
430 433
431 434 type = get_real_base_type(type);
432 435 if (!type)
433 436 return NULL;
434 437 if (type->type != SYM_STRUCT) {
435 438 snprintf(fullname, sizeof(fullname), "*%s", name);
436 439 return fullname;
437 440 }
438 441
439 442 member = get_member_from_offset(arg, offset);
440 443 if (!member || !member->ident)
441 444 return NULL;
442 445
443 446 snprintf(fullname, sizeof(fullname), "%s->%s", name, member->ident->name);
444 447 return fullname;
445 448 }
446 449
447 450 static void set_param_value(struct stree **stree, struct symbol *arg, int offset, struct range_list *rl)
448 451 {
449 452 const char *name;
450 453
451 454 name = get_name_from_offset(arg, offset);
452 455 if (!name)
453 456 return;
454 457 set_state_stree(stree, SMATCH_EXTRA, name, arg, alloc_estate_rl(rl));
455 458 }
456 459
457 460 static int save_vals(void *_db_info, int argc, char **argv, char **azColName)
458 461 {
459 462 struct db_info *db_info = _db_info;
460 463 struct symbol *type;
461 464 struct range_list *rl;
462 465 int offset = 0;
463 466 const char *value;
464 467
465 468 if (argc == 2) {
466 469 offset = atoi(argv[0]);
467 470 value = argv[1];
468 471 } else {
469 472 value = argv[0];
470 473 }
471 474
472 475 if (db_info->prev_offset != -1 &&
473 476 db_info->prev_offset != offset) {
474 477 set_param_value(&db_info->stree, db_info->arg, db_info->prev_offset, db_info->rl);
475 478 db_info->rl = NULL;
476 479 }
477 480
478 481 db_info->prev_offset = offset;
479 482
480 483 type = get_real_base_type(db_info->arg);
481 484 if (db_info->star)
482 485 goto found_type;
483 486 if (type->type != SYM_PTR)
484 487 return 0;
485 488 type = get_real_base_type(type);
486 489 if (type->type == SYM_BASETYPE)
487 490 goto found_type;
488 491 type = get_member_type_from_offset(db_info->arg, offset);
489 492 found_type:
490 493 str_to_rl(type, (char *)value, &rl);
491 494 if (db_info->rl)
492 495 db_info->rl = rl_union(db_info->rl, rl);
493 496 else
494 497 db_info->rl = rl;
495 498
496 499 return 0;
497 500 }
498 501
499 502 static struct stree *load_tag_info_sym(mtag_t tag, struct symbol *arg, int arg_offset, int star)
500 503 {
501 504 struct db_info db_info = {
502 505 .arg = arg,
503 506 .prev_offset = -1,
504 507 .star = star,
505 508 };
506 509 struct symbol *type;
507 510
508 511 if (!tag || !arg->ident)
509 512 return NULL;
510 513
511 514 type = get_real_base_type(arg);
512 515 if (!type)
513 516 return NULL;
514 517 if (!star) {
515 518 if (type->type != SYM_PTR)
516 519 return NULL;
517 520 type = get_real_base_type(type);
518 521 if (!type)
519 522 return NULL;
520 523 }
521 524
522 525 if (star || type->type == SYM_BASETYPE) {
523 526 run_sql(save_vals, &db_info,
524 527 "select value from mtag_data where tag = %lld and offset = %d and type = %d;",
525 528 tag, arg_offset, DATA_VALUE);
526 529 } else { /* presumably the parameter is a struct pointer */
527 530 run_sql(save_vals, &db_info,
528 531 "select offset, value from mtag_data where tag = %lld and type = %d;",
529 532 tag, DATA_VALUE);
530 533 }
531 534
532 535 if (db_info.prev_offset != -1)
533 536 set_param_value(&db_info.stree, arg, db_info.prev_offset, db_info.rl);
534 537
535 538 // FIXME: handle an offset correctly
536 539 if (!star && !arg_offset) {
537 540 sval_t sval;
538 541
539 542 sval.type = get_real_base_type(arg);
540 543 sval.uvalue = tag;
541 544 set_state_stree(&db_info.stree, SMATCH_EXTRA, arg->ident->name, arg, alloc_estate_sval(sval));
542 545 }
543 546 return db_info.stree;
544 547 }
545 548
546 549 static void load_container_data(struct symbol *arg, const char *info)
547 550 {
548 551 mtag_t cur_tag, container_tag, arg_tag;
549 552 int container_offset, arg_offset;
550 553 struct sm_state *sm;
551 554 struct stree *stree;
552 555 char *p, *cont;
553 556 char copy[64];
554 557 bool star = 0;
555 558
556 559 snprintf(copy, sizeof(copy), "%s", info);
557 560 p = strchr(copy, '|');
558 561 if (!p)
559 562 return;
560 563 *p = '\0';
561 564 cont = p + 1;
562 565 p = copy;
563 566 if (p[0] == '*') {
564 567 star = 1;
565 568 p += 2;
566 569 }
567 570
568 571 if (strcmp(cont, "$(-1)") != 0)
569 572 return;
570 573
571 574 if (!get_toplevel_mtag(cur_func_sym, &cur_tag))
572 575 return;
573 576
574 577 while (true) {
575 578 container_offset = strtoul(p, &p, 0);
576 579 if (local_debug)
577 580 sm_msg("%s: cur_tag = %llu container_offset = %d",
578 581 __func__, cur_tag, container_offset);
579 582 if (!mtag_map_select_container(cur_tag, container_offset, &container_tag))
580 583 return;
581 584 cur_tag = container_tag;
582 585 if (local_debug)
583 586 sm_msg("%s: container_tag = %llu p = '%s'",
584 587 __func__, container_tag, p);
585 588 if (!p)
586 589 return;
587 590 if (p[0] != '-')
588 591 break;
589 592 p++;
590 593 }
591 594
592 595 if (p[0] != '+')
593 596 return;
594 597
595 598 p++;
596 599 arg_offset = strtoul(p, &p, 0);
597 600 if (p && *p && *p != ')')
598 601 return;
599 602
600 603 if (!arg_offset || star) {
601 604 arg_tag = container_tag;
602 605 } else {
603 606 if (!mtag_map_select_tag(container_tag, -arg_offset, &arg_tag))
604 607 return;
605 608 }
606 609
607 610 stree = load_tag_info_sym(arg_tag, arg, arg_offset, star);
608 611 FOR_EACH_SM(stree, sm) {
609 612 set_state(sm->owner, sm->name, sm->sym, sm->state);
610 613 } END_FOR_EACH_SM(sm);
611 614 free_stree(&stree);
612 615 }
613 616
614 617 static void handle_passed_container(struct symbol *sym)
615 618 {
616 619 struct symbol *arg;
617 620 struct smatch_state *state;
618 621
619 622 FOR_EACH_PTR(cur_func_sym->ctype.base_type->arguments, arg) {
620 623 state = get_state(param_id, arg->ident->name, arg);
621 624 if (!state || state == &merged)
622 625 continue;
623 626 load_container_data(arg, state->name);
624 627 } END_FOR_EACH_PTR(arg);
625 628 }
626 629
627 630 void register_container_of(int id)
628 631 {
629 632 my_id = id;
630 633
631 634 add_split_return_callback(&print_returns_container_of);
632 635 add_hook(&match_call, FUNCTION_CALL_HOOK);
633 636 }
634 637
635 638 void register_container_of2(int id)
636 639 {
637 640 param_id = id;
638 641
639 642 set_dynamic_states(param_id);
640 643 select_caller_info_hook(db_passed_container, CONTAINER);
641 644 add_merge_hook(param_id, &merge_str_state);
642 645 add_hook(&handle_passed_container, AFTER_DEF_HOOK);
643 646 }
644 647
↓ open down ↓ |
352 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX