Print this page
new smatch
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/tools/smatch/src/smatch_struct_assignment.c
+++ new/usr/src/tools/smatch/src/smatch_struct_assignment.c
1 1 /*
2 2 * Copyright (C) 2014 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 /*
19 19 * This file started out by saying that if you have:
20 20 *
21 21 * struct foo one, two;
22 22 * ...
23 23 * one = two;
24 24 *
25 25 * That's equivalent to saying:
26 26 *
27 27 * one.x = two.x;
28 28 * one.y = two.y;
29 29 *
30 30 * Turning an assignment like that into a bunch of small fake assignments is
31 31 * really useful.
32 32 *
33 33 * The call to memcpy(&one, &two, sizeof(foo)); is the same as "one = two;" so
34 34 * we can re-use the code. And we may as well use it for memset() too.
35 35 * Assigning pointers is almost the same:
36 36 *
37 37 * p1 = p2;
38 38 *
39 39 * Is the same as:
40 40 *
41 41 * p1->x = p2->x;
42 42 * p1->y = p2->y;
43 43 *
44 44 * The problem is that you can go a bit crazy with pointers to pointers.
45 45 *
46 46 * p1->x->y->z->one->two->three = p2->x->y->z->one->two->three;
47 47 *
48 48 * I don't have a proper solution for this problem right now. I just copy one
49 49 * level and don't nest. It should handle limitted nesting but intelligently.
50 50 *
51 51 * The other thing is that you end up with a lot of garbage assignments where
52 52 * we record "x could be anything. x->y could be anything. x->y->z->a->b->c
53 53 * could *also* be anything!". There should be a better way to filter this
54 54 * useless information.
55 55 *
56 56 */
57 57
58 58 #include "scope.h"
59 59 #include "smatch.h"
60 60 #include "smatch_slist.h"
61 61 #include "smatch_extra.h"
62 62
63 63 enum {
64 64 COPY_NORMAL,
65 65 COPY_MEMCPY,
66 66 COPY_MEMSET,
67 67 };
68 68
69 69 static struct symbol *get_struct_type(struct expression *expr)
70 70 {
71 71 struct symbol *type;
72 72
73 73 type = get_type(expr);
74 74 if (!type)
75 75 return NULL;
76 76 if (type->type == SYM_PTR) {
77 77 type = get_real_base_type(type);
78 78 if (!type)
79 79 return NULL;
80 80 }
81 81 if (type->type == SYM_STRUCT)
82 82 return type;
83 83 if (type->type == SYM_UNION)
84 84 return type;
85 85 return NULL;
86 86 }
87 87
88 88 static struct expression *get_right_base_expr(struct symbol *left_type, struct expression *right)
89 89 {
90 90 struct symbol *struct_type;
91 91
92 92 if (!right)
93 93 return NULL;
94 94
95 95 struct_type = get_struct_type(right);
96 96 if (!struct_type)
97 97 return NULL;
98 98 if (struct_type != left_type)
99 99 return NULL;
100 100
101 101 if (right->type == EXPR_PREOP && right->op == '&')
102 102 right = strip_expr(right->unop);
103 103
104 104 if (right->type == EXPR_CALL)
105 105 return NULL;
106 106
107 107 if (is_pointer(right))
108 108 right = deref_expression(right);
109 109
110 110 return right;
111 111 }
112 112
113 113 static struct expression *remove_addr(struct expression *expr)
114 114 {
115 115 struct symbol *type;
116 116
117 117 expr = strip_expr(expr);
118 118 if (!expr)
119 119 return NULL;
120 120
121 121 if (expr->type == EXPR_PREOP && expr->op == '&')
122 122 return strip_expr(expr->unop);
123 123 type = get_type(expr);
124 124 if (!type)
125 125 return expr;
126 126 if (type->type != SYM_PTR && type->type != SYM_ARRAY)
127 127 return expr;
128 128
129 129 return deref_expression(expr);
130 130 }
131 131
132 132 static struct expression *faked_expression;
133 133 struct expression *get_faked_expression(void)
134 134 {
135 135 if (!__in_fake_assign)
136 136 return NULL;
137 137 return faked_expression;
138 138 }
139 139
140 140 static void split_fake_expr(struct expression *expr)
141 141 {
142 142 __in_fake_assign++;
143 143 __in_fake_struct_assign++;
144 144 __split_expr(expr);
145 145 __in_fake_struct_assign--;
146 146 __in_fake_assign--;
147 147 }
148 148
149 149 static void handle_non_struct_assignments(struct expression *left, struct expression *right)
150 150 {
151 151 struct symbol *type;
152 152 struct expression *assign;
153 153
154 154 type = get_type(left);
155 155 if (!type)
156 156 return;
157 157 if (type->type == SYM_PTR) {
158 158 left = deref_expression(left);
159 159 if (right)
↓ open down ↓ |
159 lines elided |
↑ open up ↑ |
160 160 right = deref_expression(right);
161 161 else
162 162 right = unknown_value_expression(left);
163 163 assign = assign_expression(left, '=', right);
164 164 split_fake_expr(assign);
165 165 return;
166 166 }
167 167 if (type->type != SYM_BASETYPE)
168 168 return;
169 169 right = strip_expr(right);
170 - if (!right)
170 + type = get_type(right);
171 + if (!right || !type || type->type == SYM_ARRAY)
171 172 right = unknown_value_expression(left);
172 173 assign = assign_expression(left, '=', right);
173 174 split_fake_expr(assign);
174 175 }
175 176
176 177 static void set_inner_struct_members(int mode, struct expression *faked, struct expression *left, struct expression *right, struct symbol *member)
177 178 {
178 179 struct expression *left_member;
179 180 struct expression *right_member = NULL; /* silence GCC */
180 181 struct expression *assign;
181 182 struct symbol *base = get_real_base_type(member);
182 183 struct symbol *tmp;
183 184
184 185 if (member->ident) {
185 186 left = member_expression(left, '.', member->ident);
186 187 if (mode != COPY_MEMSET && right)
187 188 right = member_expression(right, '.', member->ident);
188 189 }
189 190
190 191 FOR_EACH_PTR(base->symbol_list, tmp) {
191 192 struct symbol *type;
192 193
193 194 type = get_real_base_type(tmp);
194 195 if (!type)
195 196 continue;
196 197
197 198 if (type->type == SYM_ARRAY)
198 199 continue;
199 200 if (type->type == SYM_UNION || type->type == SYM_STRUCT) {
200 201 set_inner_struct_members(mode, faked, left, right, tmp);
201 202 continue;
202 203 }
203 204 if (!tmp->ident)
204 205 continue;
205 206
206 207 left_member = member_expression(left, '.', tmp->ident);
207 208
208 209 switch (mode) {
209 210 case COPY_NORMAL:
210 211 case COPY_MEMCPY:
211 212 if (right)
212 213 right_member = member_expression(right, '.', tmp->ident);
213 214 else
214 215 right_member = unknown_value_expression(left_member);
215 216 break;
216 217 case COPY_MEMSET:
217 218 right_member = right;
218 219 break;
219 220 }
220 221
221 222 assign = assign_expression(left_member, '=', right_member);
222 223 split_fake_expr(assign);
223 224 } END_FOR_EACH_PTR(tmp);
224 225 }
225 226
226 227 static void __struct_members_copy(int mode, struct expression *faked,
227 228 struct expression *left,
228 229 struct expression *right)
229 230 {
230 231 struct symbol *struct_type, *tmp, *type;
231 232 struct expression *left_member;
232 233 struct expression *right_member;
233 234 struct expression *assign;
234 235 int op = '.';
235 236
236 237
237 238 if (__in_fake_assign)
238 239 return;
239 240 faked_expression = faked;
240 241
241 242 left = strip_expr(left);
242 243 right = strip_expr(right);
243 244
244 245 struct_type = get_struct_type(left);
245 246 if (!struct_type) {
246 247 /*
247 248 * This is not a struct assignment obviously. But this is where
248 249 * memcpy() is handled so it feels like a good place to add this
249 250 * code.
250 251 */
251 252 handle_non_struct_assignments(left, right);
252 253 goto done;
253 254 }
254 255
255 256 if (is_pointer(left)) {
256 257 left = deref_expression(left);
257 258 op = '*';
258 259 }
259 260 if (mode != COPY_MEMSET)
260 261 right = get_right_base_expr(struct_type, right);
261 262
262 263 FOR_EACH_PTR(struct_type->symbol_list, tmp) {
263 264 type = get_real_base_type(tmp);
264 265 if (!type)
265 266 continue;
266 267 if (type->type == SYM_ARRAY)
267 268 continue;
268 269
269 270 if (type->type == SYM_UNION || type->type == SYM_STRUCT) {
270 271 set_inner_struct_members(mode, faked, left, right, tmp);
271 272 continue;
272 273 }
273 274
274 275 if (!tmp->ident)
275 276 continue;
276 277
277 278 left_member = member_expression(left, op, tmp->ident);
278 279 right_member = NULL;
279 280
280 281 switch (mode) {
281 282 case COPY_NORMAL:
282 283 case COPY_MEMCPY:
283 284 if (right)
284 285 right_member = member_expression(right, op, tmp->ident);
285 286 else
286 287 right_member = unknown_value_expression(left_member);
287 288 break;
288 289 case COPY_MEMSET:
289 290 right_member = right;
290 291 break;
291 292 }
292 293 if (!right_member) {
293 294 sm_perror("No right member");
294 295 continue;
295 296 }
296 297 assign = assign_expression(left_member, '=', right_member);
297 298 split_fake_expr(assign);
298 299 } END_FOR_EACH_PTR(tmp);
299 300
300 301 done:
301 302 faked_expression = NULL;
302 303 }
303 304
304 305 static int returns_zeroed_mem(struct expression *expr)
305 306 {
306 307 char *fn;
307 308
308 309 if (expr->type != EXPR_CALL || expr->fn->type != EXPR_SYMBOL)
309 310 return 0;
310 311 fn = expr_to_var(expr->fn);
311 312 if (!fn)
312 313 return 0;
313 314 if (strcmp(fn, "kcalloc") == 0)
314 315 return 1;
315 316 if (option_project == PROJ_KERNEL && strstr(fn, "zalloc"))
316 317 return 1;
317 318 return 0;
318 319 }
319 320
320 321 static int copy_containter_states(struct expression *left, struct expression *right, int offset)
321 322 {
322 323 char *left_name = NULL, *right_name = NULL;
323 324 struct symbol *left_sym, *right_sym;
324 325 struct sm_state *sm, *new_sm;
325 326 int ret = 0;
326 327 int len;
327 328 char buf[64];
328 329 char new_name[128];
329 330
330 331 right_name = expr_to_var_sym(right, &right_sym);
331 332 if (!right_name || !right_sym)
332 333 goto free;
333 334 left_name = expr_to_var_sym(left, &left_sym);
334 335 if (!left_name || !left_sym)
335 336 goto free;
336 337
337 338 len = snprintf(buf, sizeof(buf), "%s(-%d)", right_name, offset);
338 339 if (len >= sizeof(buf))
339 340 goto free;
340 341
341 342 FOR_EACH_SM(__get_cur_stree(), sm) {
342 343 if (sm->sym != right_sym)
343 344 continue;
344 345 if (strncmp(sm->name, buf, len) != 0)
345 346 continue;
346 347 snprintf(new_name, sizeof(new_name), "%s%s", left_name, sm->name + len);
347 348 new_sm = clone_sm(sm);
348 349 new_sm->name = alloc_sname(new_name);
349 350 new_sm->sym = left_sym;
350 351 __set_sm(new_sm);
351 352 ret = 1;
352 353 } END_FOR_EACH_SM(sm);
353 354 free:
354 355 free_string(left_name);
355 356 free_string(right_name);
356 357 return ret;
357 358 }
358 359
359 360 static int handle_param_offsets(struct expression *expr)
360 361 {
361 362 struct expression *right;
362 363 sval_t sval;
363 364
364 365 right = strip_expr(expr->right);
365 366
366 367 if (right->type != EXPR_BINOP || right->op != '-')
367 368 return 0;
368 369
369 370 if (!get_value(right->right, &sval))
370 371 return 0;
371 372
372 373 right = get_assigned_expr(right->left);
373 374 if (!right)
374 375 return 0;
375 376 return copy_containter_states(expr->left, right, sval.value);
376 377 }
377 378
378 379 static void returns_container_of(struct expression *expr, int param, char *key, char *value)
379 380 {
380 381 struct expression *call, *arg;
381 382 int offset;
382 383
383 384 if (expr->type != EXPR_ASSIGNMENT || expr->op != '=')
384 385 return;
385 386 call = strip_expr(expr->right);
386 387 if (call->type != EXPR_CALL)
387 388 return;
388 389 if (param != -1)
389 390 return;
390 391 param = atoi(key);
391 392 offset = atoi(value);
392 393
393 394 arg = get_argument_from_call_expr(call->args, param);
394 395 if (!arg)
395 396 return;
396 397
↓ open down ↓ |
216 lines elided |
↑ open up ↑ |
397 398 copy_containter_states(expr->left, arg, -offset);
398 399 }
399 400
400 401 void __fake_struct_member_assignments(struct expression *expr)
401 402 {
402 403 struct symbol *left_type;
403 404
404 405 if (expr->op != '=')
405 406 return;
406 407
407 - if (is_zero(expr->right))
408 + if (expr_is_zero(expr->right))
408 409 return;
409 410
410 411 left_type = get_type(expr->left);
411 412 if (!left_type ||
412 413 (left_type->type != SYM_PTR &&
413 414 left_type->type != SYM_STRUCT))
414 415 return;
415 416
416 417 if (handle_param_offsets(expr))
417 418 return;
418 419
419 420 if (returns_zeroed_mem(expr->right))
420 421 __struct_members_copy(COPY_MEMSET, expr, expr->left, zero_expr());
421 422 else
422 423 __struct_members_copy(COPY_NORMAL, expr, expr->left, expr->right);
423 424 }
424 425
425 426 static void match_memset(const char *fn, struct expression *expr, void *_size_arg)
426 427 {
427 428 struct expression *buf;
428 429 struct expression *val;
429 430
430 431 buf = get_argument_from_call_expr(expr->args, 0);
431 432 val = get_argument_from_call_expr(expr->args, 1);
432 433
433 434 buf = strip_expr(buf);
434 435 __struct_members_copy(COPY_MEMSET, expr, remove_addr(buf), val);
435 436 }
436 437
437 438 static void match_memcpy(const char *fn, struct expression *expr, void *_arg)
438 439 {
439 440 struct expression *dest;
440 441 struct expression *src;
441 442
442 443 dest = get_argument_from_call_expr(expr->args, 0);
443 444 src = get_argument_from_call_expr(expr->args, 1);
444 445
445 446 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), remove_addr(src));
446 447 }
447 448
448 449 static void match_memdup(const char *fn, struct expression *call_expr,
449 450 struct expression *expr, void *_unused)
450 451 {
451 452 struct expression *left, *right, *arg;
452 453
453 454 if (!expr || expr->type != EXPR_ASSIGNMENT)
454 455 return;
455 456
456 457 left = strip_expr(expr->left);
457 458 right = strip_expr(expr->right);
458 459
459 460 if (right->type != EXPR_CALL)
460 461 return;
461 462 arg = get_argument_from_call_expr(right->args, 0);
462 463 __struct_members_copy(COPY_MEMCPY, expr, left, arg);
463 464 }
464 465
465 466 static void match_memcpy_unknown(const char *fn, struct expression *expr, void *_arg)
466 467 {
467 468 struct expression *dest;
468 469
469 470 dest = get_argument_from_call_expr(expr->args, 0);
470 471 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(dest), NULL);
471 472 }
472 473
473 474 static void match_sscanf(const char *fn, struct expression *expr, void *unused)
474 475 {
475 476 struct expression *arg;
476 477 int i;
477 478
478 479 i = -1;
479 480 FOR_EACH_PTR(expr->args, arg) {
480 481 if (++i < 2)
481 482 continue;
482 483 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(arg), NULL);
483 484 } END_FOR_EACH_PTR(arg);
484 485 }
485 486
486 487 static void unop_expr(struct expression *expr)
487 488 {
488 489 if (expr->op != SPECIAL_INCREMENT &&
489 490 expr->op != SPECIAL_DECREMENT)
490 491 return;
491 492
492 493 if (!is_pointer(expr))
493 494 return;
494 495 faked_expression = expr;
495 496 __struct_members_copy(COPY_MEMCPY, expr, expr->unop, NULL);
496 497 faked_expression = NULL;
497 498 }
498 499
499 500 static void register_clears_param(void)
500 501 {
501 502 struct token *token;
502 503 char name[256];
503 504 const char *function;
504 505 int param;
505 506
506 507 if (option_project == PROJ_NONE)
507 508 return;
508 509
509 510 snprintf(name, 256, "%s.clears_argument", option_project_str);
510 511
511 512 token = get_tokens_file(name);
512 513 if (!token)
513 514 return;
514 515 if (token_type(token) != TOKEN_STREAMBEGIN)
515 516 return;
516 517 token = token->next;
517 518 while (token_type(token) != TOKEN_STREAMEND) {
518 519 if (token_type(token) != TOKEN_IDENT)
519 520 return;
520 521 function = show_ident(token->ident);
521 522 token = token->next;
522 523 if (token_type(token) != TOKEN_NUMBER)
523 524 return;
524 525 param = atoi(token->number);
525 526 add_function_hook(function, &match_memcpy_unknown, INT_PTR(param));
526 527 token = token->next;
527 528 }
528 529 clear_token_alloc();
529 530 }
530 531
531 532 static void db_param_cleared(struct expression *expr, int param, char *key, char *value)
532 533 {
533 534 struct expression *arg;
534 535
535 536 while (expr->type == EXPR_ASSIGNMENT)
536 537 expr = strip_expr(expr->right);
537 538 if (expr->type != EXPR_CALL)
538 539 return;
539 540
540 541 /*
541 542 * FIXME: __struct_members_copy() requires an expression but
542 543 * get_variable_from_key() returns a name/sym pair so that doesn't
543 544 * work here.
544 545 */
545 546 if (strcmp(key, "$") != 0)
546 547 return;
547 548
548 549 arg = get_argument_from_call_expr(expr->args, param);
549 550 if (!arg)
550 551 return;
551 552
552 553 if (strcmp(value, "0") == 0)
553 554 __struct_members_copy(COPY_MEMSET, expr, remove_addr(arg), zero_expr());
554 555 else
555 556 __struct_members_copy(COPY_MEMCPY, expr, remove_addr(arg), NULL);
556 557 }
557 558
558 559 void register_struct_assignment(int id)
559 560 {
560 561 add_function_hook("memset", &match_memset, NULL);
561 562 add_function_hook("__memset", &match_memset, NULL);
562 563
563 564 add_function_hook("memcpy", &match_memcpy, INT_PTR(0));
564 565 add_function_hook("memmove", &match_memcpy, INT_PTR(0));
565 566 add_function_hook("__memcpy", &match_memcpy, INT_PTR(0));
566 567 add_function_hook("__memmove", &match_memcpy, INT_PTR(0));
567 568
568 569 if (option_project == PROJ_KERNEL)
569 570 return_implies_state_sval("kmemdup", valid_ptr_min_sval, valid_ptr_max_sval, &match_memdup, NULL);
570 571
571 572 add_function_hook("sscanf", &match_sscanf, NULL);
572 573
573 574 add_hook(&unop_expr, OP_HOOK);
574 575 register_clears_param();
575 576 select_return_states_hook(PARAM_CLEARED, &db_param_cleared);
576 577
577 578 select_return_states_hook(CONTAINER, &returns_container_of);
578 579 }
↓ open down ↓ |
161 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX