1/* A state machine for detecting misuses of the malloc/free API. 2 Copyright (C) 2019-2020 Free Software Foundation, Inc. 3 Contributed by David Malcolm <dmalcolm@redhat.com>. 4 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it 8under the terms of the GNU General Public License as published by 9the Free Software Foundation; either version 3, or (at your option) 10any later version. 11 12GCC is distributed in the hope that it will be useful, but 13WITHOUT ANY WARRANTY; without even the implied warranty of 14MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15General Public License for more details. 16 17You should have received a copy of the GNU General Public License 18along with GCC; see the file COPYING3. If not see 19<http://www.gnu.org/licenses/>. */ 20 21#include "config.h" 22#include "system.h" 23#include "coretypes.h" 24#include "tree.h" 25#include "function.h" 26#include "basic-block.h" 27#include "gimple.h" 28#include "options.h" 29#include "bitmap.h" 30#include "diagnostic-path.h" 31#include "diagnostic-metadata.h" 32#include "function.h" 33#include "analyzer/analyzer.h" 34#include "diagnostic-event-id.h" 35#include "analyzer/analyzer-logging.h" 36#include "analyzer/sm.h" 37#include "analyzer/pending-diagnostic.h" 38 39#if ENABLE_ANALYZER 40 41namespace ana { 42 43namespace { 44 45/* A state machine for detecting misuses of the malloc/free API. 46 47 See sm-malloc.dot for an overview (keep this in-sync with that file). */ 48 49class malloc_state_machine : public state_machine 50{ 51public: 52 malloc_state_machine (logger *logger); 53 54 bool inherited_state_p () const FINAL OVERRIDE { return false; } 55 56 bool on_stmt (sm_context *sm_ctxt, 57 const supernode *node, 58 const gimple *stmt) const FINAL OVERRIDE; 59 60 void on_phi (sm_context *sm_ctxt, 61 const supernode *node, 62 const gphi *phi, 63 tree rhs) const FINAL OVERRIDE; 64 65 void on_condition (sm_context *sm_ctxt, 66 const supernode *node, 67 const gimple *stmt, 68 tree lhs, 69 enum tree_code op, 70 tree rhs) const FINAL OVERRIDE; 71 72 bool can_purge_p (state_t s) const FINAL OVERRIDE; 73 pending_diagnostic *on_leak (tree var) const FINAL OVERRIDE; 74 75 /* Start state. */ 76 state_t m_start; 77 78 /* State for a pointer returned from malloc that hasn't been checked for 79 NULL. 80 It could be a pointer to heap-allocated memory, or could be NULL. */ 81 state_t m_unchecked; 82 83 /* State for a pointer that's known to be NULL. */ 84 state_t m_null; 85 86 /* State for a pointer to heap-allocated memory, known to be non-NULL. */ 87 state_t m_nonnull; 88 89 /* State for a pointer to freed memory. */ 90 state_t m_freed; 91 92 /* State for a pointer that's known to not be on the heap (e.g. to a local 93 or global). */ 94 state_t m_non_heap; // TODO: or should this be a different state machine? 95 // or do we need child values etc? 96 97 /* Stop state, for pointers we don't want to track any more. */ 98 state_t m_stop; 99 100private: 101 void on_zero_assignment (sm_context *sm_ctxt, 102 const supernode *node, 103 const gimple *stmt, 104 tree lhs) const; 105}; 106 107/* Class for diagnostics relating to malloc_state_machine. */ 108 109class malloc_diagnostic : public pending_diagnostic 110{ 111public: 112 malloc_diagnostic (const malloc_state_machine &sm, tree arg) 113 : m_sm (sm), m_arg (arg) 114 {} 115 116 bool subclass_equal_p (const pending_diagnostic &base_other) const OVERRIDE 117 { 118 return same_tree_p (m_arg, ((const malloc_diagnostic &)base_other).m_arg); 119 } 120 121 label_text describe_state_change (const evdesc::state_change &change) 122 OVERRIDE 123 { 124 if (change.m_old_state == m_sm.m_start 125 && change.m_new_state == m_sm.m_unchecked) 126 // TODO: verify that it's the allocation stmt, not a copy 127 return label_text::borrow ("allocated here"); 128 if (change.m_old_state == m_sm.m_unchecked 129 && change.m_new_state == m_sm.m_nonnull) 130 return change.formatted_print ("assuming %qE is non-NULL", 131 change.m_expr); 132 if (change.m_new_state == m_sm.m_null) 133 { 134 if (change.m_old_state == m_sm.m_unchecked) 135 return change.formatted_print ("assuming %qE is NULL", 136 change.m_expr); 137 else 138 return change.formatted_print ("%qE is NULL", 139 change.m_expr); 140 } 141 142 return label_text (); 143 } 144 145protected: 146 const malloc_state_machine &m_sm; 147 tree m_arg; 148}; 149 150/* Concrete subclass for reporting double-free diagnostics. */ 151 152class double_free : public malloc_diagnostic 153{ 154public: 155 double_free (const malloc_state_machine &sm, tree arg) 156 : malloc_diagnostic (sm, arg) 157 {} 158 159 const char *get_kind () const FINAL OVERRIDE { return "double_free"; } 160 161 bool emit (rich_location *rich_loc) FINAL OVERRIDE 162 { 163 auto_diagnostic_group d; 164 diagnostic_metadata m; 165 m.add_cwe (415); /* CWE-415: Double Free. */ 166 return warning_meta (rich_loc, m, OPT_Wanalyzer_double_free, 167 "double-%<free%> of %qE", m_arg); 168 } 169 170 label_text describe_state_change (const evdesc::state_change &change) 171 FINAL OVERRIDE 172 { 173 if (change.m_new_state == m_sm.m_freed) 174 { 175 m_first_free_event = change.m_event_id; 176 return change.formatted_print ("first %qs here", "free"); 177 } 178 return malloc_diagnostic::describe_state_change (change); 179 } 180 181 label_text describe_call_with_state (const evdesc::call_with_state &info) 182 FINAL OVERRIDE 183 { 184 if (info.m_state == m_sm.m_freed) 185 return info.formatted_print 186 ("passing freed pointer %qE in call to %qE from %qE", 187 info.m_expr, info.m_callee_fndecl, info.m_caller_fndecl); 188 return label_text (); 189 } 190 191 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE 192 { 193 if (m_first_free_event.known_p ()) 194 return ev.formatted_print ("second %qs here; first %qs was at %@", 195 "free", "free", 196 &m_first_free_event); 197 return ev.formatted_print ("second %qs here", "free"); 198 } 199 200private: 201 diagnostic_event_id_t m_first_free_event; 202}; 203 204/* Abstract subclass for describing possible bad uses of NULL. 205 Responsible for describing the call that could return NULL. */ 206 207class possible_null : public malloc_diagnostic 208{ 209public: 210 possible_null (const malloc_state_machine &sm, tree arg) 211 : malloc_diagnostic (sm, arg) 212 {} 213 214 label_text describe_state_change (const evdesc::state_change &change) 215 FINAL OVERRIDE 216 { 217 if (change.m_old_state == m_sm.m_start 218 && change.m_new_state == m_sm.m_unchecked) 219 { 220 m_origin_of_unchecked_event = change.m_event_id; 221 return label_text::borrow ("this call could return NULL"); 222 } 223 return malloc_diagnostic::describe_state_change (change); 224 } 225 226 label_text describe_return_of_state (const evdesc::return_of_state &info) 227 FINAL OVERRIDE 228 { 229 if (info.m_state == m_sm.m_unchecked) 230 return info.formatted_print ("possible return of NULL to %qE from %qE", 231 info.m_caller_fndecl, info.m_callee_fndecl); 232 return label_text (); 233 } 234 235protected: 236 diagnostic_event_id_t m_origin_of_unchecked_event; 237}; 238 239/* Concrete subclass for describing dereference of a possible NULL 240 value. */ 241 242class possible_null_deref : public possible_null 243{ 244public: 245 possible_null_deref (const malloc_state_machine &sm, tree arg) 246 : possible_null (sm, arg) 247 {} 248 249 const char *get_kind () const FINAL OVERRIDE { return "possible_null_deref"; } 250 251 bool emit (rich_location *rich_loc) FINAL OVERRIDE 252 { 253 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */ 254 diagnostic_metadata m; 255 m.add_cwe (690); 256 return warning_meta (rich_loc, m, 257 OPT_Wanalyzer_possible_null_dereference, 258 "dereference of possibly-NULL %qE", m_arg); 259 } 260 261 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE 262 { 263 if (m_origin_of_unchecked_event.known_p ()) 264 return ev.formatted_print ("%qE could be NULL: unchecked value from %@", 265 ev.m_expr, 266 &m_origin_of_unchecked_event); 267 else 268 return ev.formatted_print ("%qE could be NULL", ev.m_expr); 269 } 270 271}; 272 273/* Subroutine for use by possible_null_arg::emit and null_arg::emit. 274 Issue a note informing that the pertinent argument must be non-NULL. */ 275 276static void 277inform_nonnull_attribute (tree fndecl, int arg_idx) 278{ 279 inform (DECL_SOURCE_LOCATION (fndecl), 280 "argument %u of %qD must be non-null", 281 arg_idx + 1, fndecl); 282 /* Ideally we would use the location of the parm and underline the 283 attribute also - but we don't have the location_t values at this point 284 in the middle-end. 285 For reference, the C and C++ FEs have get_fndecl_argument_location. */ 286} 287 288/* Concrete subclass for describing passing a possibly-NULL value to a 289 function marked with __attribute__((nonnull)). */ 290 291class possible_null_arg : public possible_null 292{ 293public: 294 possible_null_arg (const malloc_state_machine &sm, tree arg, 295 tree fndecl, int arg_idx) 296 : possible_null (sm, arg), 297 m_fndecl (fndecl), m_arg_idx (arg_idx) 298 {} 299 300 const char *get_kind () const FINAL OVERRIDE { return "possible_null_arg"; } 301 302 bool subclass_equal_p (const pending_diagnostic &base_other) const 303 { 304 const possible_null_arg &sub_other 305 = (const possible_null_arg &)base_other; 306 return (same_tree_p (m_arg, sub_other.m_arg) 307 && m_fndecl == sub_other.m_fndecl 308 && m_arg_idx == sub_other.m_arg_idx); 309 } 310 311 312 bool emit (rich_location *rich_loc) FINAL OVERRIDE 313 { 314 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */ 315 auto_diagnostic_group d; 316 diagnostic_metadata m; 317 m.add_cwe (690); 318 bool warned 319 = warning_meta (rich_loc, m, OPT_Wanalyzer_possible_null_argument, 320 "use of possibly-NULL %qE where non-null expected", 321 m_arg); 322 if (warned) 323 inform_nonnull_attribute (m_fndecl, m_arg_idx); 324 return warned; 325 } 326 327 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE 328 { 329 if (m_origin_of_unchecked_event.known_p ()) 330 return ev.formatted_print ("argument %u (%qE) from %@ could be NULL" 331 " where non-null expected", 332 m_arg_idx + 1, ev.m_expr, 333 &m_origin_of_unchecked_event); 334 else 335 return ev.formatted_print ("argument %u (%qE) could be NULL" 336 " where non-null expected", 337 m_arg_idx + 1, ev.m_expr); 338 } 339 340private: 341 tree m_fndecl; 342 int m_arg_idx; 343}; 344 345/* Concrete subclass for describing a dereference of a NULL value. */ 346 347class null_deref : public malloc_diagnostic 348{ 349public: 350 null_deref (const malloc_state_machine &sm, tree arg) 351 : malloc_diagnostic (sm, arg) {} 352 353 const char *get_kind () const FINAL OVERRIDE { return "null_deref"; } 354 355 bool emit (rich_location *rich_loc) FINAL OVERRIDE 356 { 357 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */ 358 diagnostic_metadata m; 359 m.add_cwe (690); 360 return warning_meta (rich_loc, m, 361 OPT_Wanalyzer_null_dereference, 362 "dereference of NULL %qE", m_arg); 363 } 364 365 label_text describe_return_of_state (const evdesc::return_of_state &info) 366 FINAL OVERRIDE 367 { 368 if (info.m_state == m_sm.m_null) 369 return info.formatted_print ("return of NULL to %qE from %qE", 370 info.m_caller_fndecl, info.m_callee_fndecl); 371 return label_text (); 372 } 373 374 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE 375 { 376 return ev.formatted_print ("dereference of NULL %qE", ev.m_expr); 377 } 378}; 379 380/* Concrete subclass for describing passing a NULL value to a 381 function marked with __attribute__((nonnull)). */ 382 383class null_arg : public malloc_diagnostic 384{ 385public: 386 null_arg (const malloc_state_machine &sm, tree arg, 387 tree fndecl, int arg_idx) 388 : malloc_diagnostic (sm, arg), 389 m_fndecl (fndecl), m_arg_idx (arg_idx) 390 {} 391 392 const char *get_kind () const FINAL OVERRIDE { return "null_arg"; } 393 394 bool subclass_equal_p (const pending_diagnostic &base_other) const 395 { 396 const null_arg &sub_other 397 = (const null_arg &)base_other; 398 return (same_tree_p (m_arg, sub_other.m_arg) 399 && m_fndecl == sub_other.m_fndecl 400 && m_arg_idx == sub_other.m_arg_idx); 401 } 402 403 bool emit (rich_location *rich_loc) FINAL OVERRIDE 404 { 405 /* CWE-690: Unchecked Return Value to NULL Pointer Dereference. */ 406 auto_diagnostic_group d; 407 diagnostic_metadata m; 408 m.add_cwe (690); 409 bool warned = warning_meta (rich_loc, m, OPT_Wanalyzer_null_argument, 410 "use of NULL %qE where non-null expected", 411 m_arg); 412 if (warned) 413 inform_nonnull_attribute (m_fndecl, m_arg_idx); 414 return warned; 415 } 416 417 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE 418 { 419 return ev.formatted_print ("argument %u (%qE) NULL" 420 " where non-null expected", 421 m_arg_idx + 1, ev.m_expr); 422 } 423 424private: 425 tree m_fndecl; 426 int m_arg_idx; 427}; 428 429class use_after_free : public malloc_diagnostic 430{ 431public: 432 use_after_free (const malloc_state_machine &sm, tree arg) 433 : malloc_diagnostic (sm, arg) 434 {} 435 436 const char *get_kind () const FINAL OVERRIDE { return "use_after_free"; } 437 438 bool emit (rich_location *rich_loc) FINAL OVERRIDE 439 { 440 /* CWE-416: Use After Free. */ 441 diagnostic_metadata m; 442 m.add_cwe (416); 443 return warning_meta (rich_loc, m, OPT_Wanalyzer_use_after_free, 444 "use after %<free%> of %qE", m_arg); 445 } 446 447 label_text describe_state_change (const evdesc::state_change &change) 448 FINAL OVERRIDE 449 { 450 if (change.m_new_state == m_sm.m_freed) 451 { 452 m_free_event = change.m_event_id; 453 return label_text::borrow ("freed here"); 454 } 455 return malloc_diagnostic::describe_state_change (change); 456 } 457 458 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE 459 { 460 if (m_free_event.known_p ()) 461 return ev.formatted_print ("use after %<free%> of %qE; freed at %@", 462 ev.m_expr, &m_free_event); 463 else 464 return ev.formatted_print ("use after %<free%> of %qE", ev.m_expr); 465 } 466 467private: 468 diagnostic_event_id_t m_free_event; 469}; 470 471class malloc_leak : public malloc_diagnostic 472{ 473public: 474 malloc_leak (const malloc_state_machine &sm, tree arg) 475 : malloc_diagnostic (sm, arg) {} 476 477 const char *get_kind () const FINAL OVERRIDE { return "malloc_leak"; } 478 479 bool emit (rich_location *rich_loc) FINAL OVERRIDE 480 { 481 diagnostic_metadata m; 482 m.add_cwe (401); 483 return warning_meta (rich_loc, m, OPT_Wanalyzer_malloc_leak, 484 "leak of %qE", m_arg); 485 } 486 487 label_text describe_state_change (const evdesc::state_change &change) 488 FINAL OVERRIDE 489 { 490 if (change.m_new_state == m_sm.m_unchecked) 491 { 492 m_malloc_event = change.m_event_id; 493 return label_text::borrow ("allocated here"); 494 } 495 return malloc_diagnostic::describe_state_change (change); 496 } 497 498 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE 499 { 500 if (m_malloc_event.known_p ()) 501 return ev.formatted_print ("%qE leaks here; was allocated at %@", 502 ev.m_expr, &m_malloc_event); 503 else 504 return ev.formatted_print ("%qE leaks here", ev.m_expr); 505 } 506 507private: 508 diagnostic_event_id_t m_malloc_event; 509}; 510 511class free_of_non_heap : public malloc_diagnostic 512{ 513public: 514 free_of_non_heap (const malloc_state_machine &sm, tree arg) 515 : malloc_diagnostic (sm, arg), m_kind (KIND_UNKNOWN) 516 { 517 } 518 519 const char *get_kind () const FINAL OVERRIDE { return "free_of_non_heap"; } 520 521 bool subclass_equal_p (const pending_diagnostic &base_other) const 522 FINAL OVERRIDE 523 { 524 const free_of_non_heap &other = (const free_of_non_heap &)base_other; 525 return (same_tree_p (m_arg, other.m_arg) && m_kind == other.m_kind); 526 } 527 528 bool emit (rich_location *rich_loc) FINAL OVERRIDE 529 { 530 auto_diagnostic_group d; 531 diagnostic_metadata m; 532 m.add_cwe (590); /* CWE-590: Free of Memory not on the Heap. */ 533 switch (m_kind) 534 { 535 default: 536 gcc_unreachable (); 537 case KIND_UNKNOWN: 538 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap, 539 "%<free%> of %qE which points to memory" 540 " not on the heap", 541 m_arg); 542 break; 543 case KIND_ALLOCA: 544 return warning_meta (rich_loc, m, OPT_Wanalyzer_free_of_non_heap, 545 "%<free%> of memory allocated on the stack by" 546 " %qs (%qE) will corrupt the heap", 547 "alloca", m_arg); 548 break; 549 } 550 } 551 552 label_text describe_state_change (const evdesc::state_change &change) 553 FINAL OVERRIDE 554 { 555 /* Attempt to reconstruct what kind of pointer it is. 556 (It seems neater for this to be a part of the state, though). */ 557 if (TREE_CODE (change.m_expr) == SSA_NAME) 558 { 559 gimple *def_stmt = SSA_NAME_DEF_STMT (change.m_expr); 560 if (gcall *call = dyn_cast <gcall *> (def_stmt)) 561 { 562 if (is_special_named_call_p (call, "alloca", 1) 563 || is_special_named_call_p (call, "__builtin_alloca", 1)) 564 { 565 m_kind = KIND_ALLOCA; 566 return label_text::borrow 567 ("memory is allocated on the stack here"); 568 } 569 } 570 } 571 return label_text::borrow ("pointer is from here"); 572 } 573 574 label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE 575 { 576 return ev.formatted_print ("call to %qs here", "free"); 577 } 578 579private: 580 enum kind 581 { 582 KIND_UNKNOWN, 583 KIND_ALLOCA 584 }; 585 enum kind m_kind; 586}; 587 588/* malloc_state_machine's ctor. */ 589 590malloc_state_machine::malloc_state_machine (logger *logger) 591: state_machine ("malloc", logger) 592{ 593 m_start = add_state ("start"); 594 m_unchecked = add_state ("unchecked"); 595 m_null = add_state ("null"); 596 m_nonnull = add_state ("nonnull"); 597 m_freed = add_state ("freed"); 598 m_non_heap = add_state ("non-heap"); 599 m_stop = add_state ("stop"); 600} 601 602/* Implementation of state_machine::on_stmt vfunc for malloc_state_machine. */ 603 604bool 605malloc_state_machine::on_stmt (sm_context *sm_ctxt, 606 const supernode *node, 607 const gimple *stmt) const 608{ 609 if (const gcall *call = dyn_cast <const gcall *> (stmt)) 610 if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call)) 611 { 612 if (is_named_call_p (callee_fndecl, "malloc", call, 1) 613 || is_named_call_p (callee_fndecl, "calloc", call, 2) 614 || is_std_named_call_p (callee_fndecl, "malloc", call, 1) 615 || is_std_named_call_p (callee_fndecl, "calloc", call, 2) 616 || is_named_call_p (callee_fndecl, "__builtin_malloc", call, 1) 617 || is_named_call_p (callee_fndecl, "__builtin_calloc", call, 2)) 618 { 619 tree lhs = gimple_call_lhs (call); 620 if (lhs) 621 { 622 lhs = sm_ctxt->get_readable_tree (lhs); 623 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_unchecked); 624 } 625 else 626 { 627 /* TODO: report leak. */ 628 } 629 return true; 630 } 631 632 if (is_named_call_p (callee_fndecl, "alloca", call, 1) 633 || is_named_call_p (callee_fndecl, "__builtin_alloca", call, 1)) 634 { 635 tree lhs = gimple_call_lhs (call); 636 if (lhs) 637 { 638 lhs = sm_ctxt->get_readable_tree (lhs); 639 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap); 640 } 641 return true; 642 } 643 644 if (is_named_call_p (callee_fndecl, "free", call, 1) 645 || is_std_named_call_p (callee_fndecl, "free", call, 1) 646 || is_named_call_p (callee_fndecl, "__builtin_free", call, 1)) 647 { 648 tree arg = gimple_call_arg (call, 0); 649 650 arg = sm_ctxt->get_readable_tree (arg); 651 652 /* start/unchecked/nonnull -> freed. */ 653 sm_ctxt->on_transition (node, stmt, arg, m_start, m_freed); 654 sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_freed); 655 sm_ctxt->on_transition (node, stmt, arg, m_nonnull, m_freed); 656 657 /* Keep state "null" as-is, rather than transitioning to "free"; 658 we don't want to complain about double-free of NULL. */ 659 660 /* freed -> stop, with warning. */ 661 sm_ctxt->warn_for_state (node, stmt, arg, m_freed, 662 new double_free (*this, arg)); 663 sm_ctxt->on_transition (node, stmt, arg, m_freed, m_stop); 664 665 /* non-heap -> stop, with warning. */ 666 sm_ctxt->warn_for_state (node, stmt, arg, m_non_heap, 667 new free_of_non_heap (*this, arg)); 668 sm_ctxt->on_transition (node, stmt, arg, m_non_heap, m_stop); 669 return true; 670 } 671 672 /* Handle "__attribute__((nonnull))". */ 673 { 674 tree fntype = TREE_TYPE (callee_fndecl); 675 bitmap nonnull_args = get_nonnull_args (fntype); 676 if (nonnull_args) 677 { 678 for (unsigned i = 0; i < gimple_call_num_args (stmt); i++) 679 { 680 tree arg = gimple_call_arg (stmt, i); 681 if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE) 682 continue; 683 /* If we have a nonnull-args, and either all pointers, or just 684 the specified pointers. */ 685 if (bitmap_empty_p (nonnull_args) 686 || bitmap_bit_p (nonnull_args, i)) 687 { 688 sm_ctxt->warn_for_state 689 (node, stmt, arg, m_unchecked, 690 new possible_null_arg (*this, arg, callee_fndecl, i)); 691 sm_ctxt->on_transition (node, stmt, arg, m_unchecked, 692 m_nonnull); 693 694 sm_ctxt->warn_for_state 695 (node, stmt, arg, m_null, 696 new null_arg (*this, arg, callee_fndecl, i)); 697 sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop); 698 } 699 } 700 BITMAP_FREE (nonnull_args); 701 } 702 } 703 } 704 705 if (tree lhs = is_zero_assignment (stmt)) 706 if (any_pointer_p (lhs)) 707 on_zero_assignment (sm_ctxt, node, stmt,lhs); 708 709 if (const gassign *assign_stmt = dyn_cast <const gassign *> (stmt)) 710 { 711 enum tree_code op = gimple_assign_rhs_code (assign_stmt); 712 if (op == ADDR_EXPR) 713 { 714 tree lhs = gimple_assign_lhs (assign_stmt); 715 if (lhs) 716 { 717 lhs = sm_ctxt->get_readable_tree (lhs); 718 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_non_heap); 719 } 720 } 721 } 722 723 /* Handle dereferences. */ 724 for (unsigned i = 0; i < gimple_num_ops (stmt); i++) 725 { 726 tree op = gimple_op (stmt, i); 727 if (!op) 728 continue; 729 if (TREE_CODE (op) == COMPONENT_REF) 730 op = TREE_OPERAND (op, 0); 731 732 if (TREE_CODE (op) == MEM_REF) 733 { 734 tree arg = TREE_OPERAND (op, 0); 735 arg = sm_ctxt->get_readable_tree (arg); 736 737 sm_ctxt->warn_for_state (node, stmt, arg, m_unchecked, 738 new possible_null_deref (*this, arg)); 739 sm_ctxt->on_transition (node, stmt, arg, m_unchecked, m_nonnull); 740 741 sm_ctxt->warn_for_state (node, stmt, arg, m_null, 742 new null_deref (*this, arg)); 743 sm_ctxt->on_transition (node, stmt, arg, m_null, m_stop); 744 745 sm_ctxt->warn_for_state (node, stmt, arg, m_freed, 746 new use_after_free (*this, arg)); 747 sm_ctxt->on_transition (node, stmt, arg, m_freed, m_stop); 748 } 749 } 750 return false; 751} 752 753/* Implementation of state_machine::on_phi vfunc for malloc_state_machine. */ 754 755void 756malloc_state_machine::on_phi (sm_context *sm_ctxt, 757 const supernode *node, 758 const gphi *phi, 759 tree rhs) const 760{ 761 if (zerop (rhs)) 762 { 763 tree lhs = gimple_phi_result (phi); 764 on_zero_assignment (sm_ctxt, node, phi, lhs); 765 } 766} 767 768/* Implementation of state_machine::on_condition vfunc for malloc_state_machine. 769 Potentially transition state 'unchecked' to 'nonnull' or to 'null'. */ 770 771void 772malloc_state_machine::on_condition (sm_context *sm_ctxt, 773 const supernode *node, 774 const gimple *stmt, 775 tree lhs, 776 enum tree_code op, 777 tree rhs) const 778{ 779 if (!zerop (rhs)) 780 return; 781 782 if (!any_pointer_p (lhs)) 783 return; 784 if (!any_pointer_p (rhs)) 785 return; 786 787 if (op == NE_EXPR) 788 { 789 log ("got 'ARG != 0' match"); 790 sm_ctxt->on_transition (node, stmt, 791 lhs, m_unchecked, m_nonnull); 792 } 793 else if (op == EQ_EXPR) 794 { 795 log ("got 'ARG == 0' match"); 796 sm_ctxt->on_transition (node, stmt, 797 lhs, m_unchecked, m_null); 798 } 799} 800 801/* Implementation of state_machine::can_purge_p vfunc for malloc_state_machine. 802 Don't allow purging of pointers in state 'unchecked' or 'nonnull' 803 (to avoid false leak reports). */ 804 805bool 806malloc_state_machine::can_purge_p (state_t s) const 807{ 808 return s != m_unchecked && s != m_nonnull; 809} 810 811/* Implementation of state_machine::on_leak vfunc for malloc_state_machine 812 (for complaining about leaks of pointers in state 'unchecked' and 813 'nonnull'). */ 814 815pending_diagnostic * 816malloc_state_machine::on_leak (tree var) const 817{ 818 return new malloc_leak (*this, var); 819} 820 821/* Shared logic for handling GIMPLE_ASSIGNs and GIMPLE_PHIs that 822 assign zero to LHS. */ 823 824void 825malloc_state_machine::on_zero_assignment (sm_context *sm_ctxt, 826 const supernode *node, 827 const gimple *stmt, 828 tree lhs) const 829{ 830 sm_ctxt->on_transition (node, stmt, lhs, m_start, m_null); 831 sm_ctxt->on_transition (node, stmt, lhs, m_unchecked, m_null); 832 sm_ctxt->on_transition (node, stmt, lhs, m_nonnull, m_null); 833 sm_ctxt->on_transition (node, stmt, lhs, m_freed, m_null); 834} 835 836} // anonymous namespace 837 838/* Internal interface to this file. */ 839 840state_machine * 841make_malloc_state_machine (logger *logger) 842{ 843 return new malloc_state_machine (logger); 844} 845 846} // namespace ana 847 848#endif /* #if ENABLE_ANALYZER */ 849