|
48 | 48 | #include "InterCheckerAPI.h"
|
49 | 49 | #include "clang/AST/Attr.h"
|
50 | 50 | #include "clang/AST/DeclCXX.h"
|
| 51 | +#include "clang/AST/DeclTemplate.h" |
51 | 52 | #include "clang/AST/Expr.h"
|
52 | 53 | #include "clang/AST/ExprCXX.h"
|
53 | 54 | #include "clang/AST/ParentMap.h"
|
|
64 | 65 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
65 | 66 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
|
66 | 67 | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
|
| 68 | +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" |
67 | 69 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
|
68 | 70 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
|
69 | 71 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
|
70 | 72 | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
|
| 73 | +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" |
71 | 74 | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
|
72 | 75 | #include "llvm/ADT/STLExtras.h"
|
| 76 | +#include "llvm/ADT/SetOperations.h" |
73 | 77 | #include "llvm/ADT/SmallString.h"
|
74 | 78 | #include "llvm/ADT/StringExtras.h"
|
75 | 79 | #include "llvm/Support/Compiler.h"
|
@@ -298,6 +302,8 @@ class MallocChecker
|
298 | 302 | /// which might free a pointer are annotated.
|
299 | 303 | DefaultBool ShouldIncludeOwnershipAnnotatedFunctions;
|
300 | 304 |
|
| 305 | + DefaultBool ShouldRegisterNoOwnershipChangeVisitor; |
| 306 | + |
301 | 307 | /// Many checkers are essentially built into this one, so enabling them will
|
302 | 308 | /// make MallocChecker perform additional modeling and reporting.
|
303 | 309 | enum CheckKind {
|
@@ -722,11 +728,146 @@ class MallocChecker
|
722 | 728 | bool isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C,
|
723 | 729 | SVal ArgVal) const;
|
724 | 730 | };
|
| 731 | +} // end anonymous namespace |
| 732 | + |
| 733 | +//===----------------------------------------------------------------------===// |
| 734 | +// Definition of NoOwnershipChangeVisitor. |
| 735 | +//===----------------------------------------------------------------------===// |
| 736 | + |
| 737 | +namespace { |
| 738 | +class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor { |
| 739 | + SymbolRef Sym; |
| 740 | + using OwnerSet = llvm::SmallPtrSet<const MemRegion *, 8>; |
| 741 | + |
| 742 | + // Collect which entities point to the allocated memory, and could be |
| 743 | + // responsible for deallocating it. |
| 744 | + class OwnershipBindingsHandler : public StoreManager::BindingsHandler { |
| 745 | + SymbolRef Sym; |
| 746 | + OwnerSet &Owners; |
| 747 | + |
| 748 | + public: |
| 749 | + OwnershipBindingsHandler(SymbolRef Sym, OwnerSet &Owners) |
| 750 | + : Sym(Sym), Owners(Owners) {} |
| 751 | + |
| 752 | + bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *Region, |
| 753 | + SVal Val) override { |
| 754 | + if (Val.getAsSymbol() == Sym) |
| 755 | + Owners.insert(Region); |
| 756 | + return true; |
| 757 | + } |
| 758 | + }; |
| 759 | + |
| 760 | +protected: |
| 761 | + OwnerSet getOwnersAtNode(const ExplodedNode *N) { |
| 762 | + OwnerSet Ret; |
| 763 | + |
| 764 | + ProgramStateRef State = N->getState(); |
| 765 | + OwnershipBindingsHandler Handler{Sym, Ret}; |
| 766 | + State->getStateManager().getStoreManager().iterBindings(State->getStore(), |
| 767 | + Handler); |
| 768 | + return Ret; |
| 769 | + } |
| 770 | + |
| 771 | + static const ExplodedNode *getCallExitEnd(const ExplodedNode *N) { |
| 772 | + while (N && !N->getLocationAs<CallExitEnd>()) |
| 773 | + N = N->getFirstSucc(); |
| 774 | + return N; |
| 775 | + } |
| 776 | + |
| 777 | + virtual bool |
| 778 | + wasModifiedBeforeCallExit(const ExplodedNode *CurrN, |
| 779 | + const ExplodedNode *CallExitN) override { |
| 780 | + if (CurrN->getLocationAs<CallEnter>()) |
| 781 | + return true; |
| 782 | + |
| 783 | + // Its the state right *after* the call that is interesting. Any pointers |
| 784 | + // inside the call that pointed to the allocated memory are of little |
| 785 | + // consequence if their lifetime ends within the function. |
| 786 | + CallExitN = getCallExitEnd(CallExitN); |
| 787 | + if (!CallExitN) |
| 788 | + return true; |
| 789 | + |
| 790 | + if (CurrN->getState()->get<RegionState>(Sym) != |
| 791 | + CallExitN->getState()->get<RegionState>(Sym)) |
| 792 | + return true; |
| 793 | + |
| 794 | + OwnerSet CurrOwners = getOwnersAtNode(CurrN); |
| 795 | + OwnerSet ExitOwners = getOwnersAtNode(CallExitN); |
| 796 | + |
| 797 | + // Owners in the current set may be purged from the analyzer later on. |
| 798 | + // If a variable is dead (is not referenced directly or indirectly after |
| 799 | + // some point), it will be removed from the Store before the end of its |
| 800 | + // actual lifetime. |
| 801 | + // This means that that if the ownership status didn't change, CurrOwners |
| 802 | + // must be a superset of, but not necessarily equal to ExitOwners. |
| 803 | + return !llvm::set_is_subset(ExitOwners, CurrOwners); |
| 804 | + } |
| 805 | + |
| 806 | + static PathDiagnosticPieceRef emitNote(const ExplodedNode *N) { |
| 807 | + PathDiagnosticLocation L = PathDiagnosticLocation::create( |
| 808 | + N->getLocation(), |
| 809 | + N->getState()->getStateManager().getContext().getSourceManager()); |
| 810 | + return std::make_shared<PathDiagnosticEventPiece>( |
| 811 | + L, "Returning without deallocating memory or storing the pointer for " |
| 812 | + "later deallocation"); |
| 813 | + } |
| 814 | + |
| 815 | + virtual PathDiagnosticPieceRef |
| 816 | + maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R, |
| 817 | + const ObjCMethodCall &Call, |
| 818 | + const ExplodedNode *N) override { |
| 819 | + // TODO: Implement. |
| 820 | + return nullptr; |
| 821 | + } |
| 822 | + |
| 823 | + virtual PathDiagnosticPieceRef |
| 824 | + maybeEmitNoteForCXXThis(PathSensitiveBugReport &R, |
| 825 | + const CXXConstructorCall &Call, |
| 826 | + const ExplodedNode *N) override { |
| 827 | + // TODO: Implement. |
| 828 | + return nullptr; |
| 829 | + } |
| 830 | + |
| 831 | + virtual PathDiagnosticPieceRef |
| 832 | + maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call, |
| 833 | + const ExplodedNode *N) override { |
| 834 | + // TODO: Factor the logic of "what constitutes as an entity being passed |
| 835 | + // into a function call" out by reusing the code in |
| 836 | + // NoStoreFuncVisitor::maybeEmitNoteForParameters, maybe by incorporating |
| 837 | + // the printing technology in UninitializedObject's FieldChainInfo. |
| 838 | + ArrayRef<ParmVarDecl *> Parameters = Call.parameters(); |
| 839 | + for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) { |
| 840 | + SVal V = Call.getArgSVal(I); |
| 841 | + if (V.getAsSymbol() == Sym) |
| 842 | + return emitNote(N); |
| 843 | + } |
| 844 | + return nullptr; |
| 845 | + } |
| 846 | + |
| 847 | +public: |
| 848 | + NoOwnershipChangeVisitor(SymbolRef Sym) |
| 849 | + : NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough), |
| 850 | + Sym(Sym) {} |
| 851 | + |
| 852 | + void Profile(llvm::FoldingSetNodeID &ID) const override { |
| 853 | + static int Tag = 0; |
| 854 | + ID.AddPointer(&Tag); |
| 855 | + ID.AddPointer(Sym); |
| 856 | + } |
| 857 | + |
| 858 | + void *getTag() const { |
| 859 | + static int Tag = 0; |
| 860 | + return static_cast<void *>(&Tag); |
| 861 | + } |
| 862 | +}; |
| 863 | + |
| 864 | +} // end anonymous namespace |
725 | 865 |
|
726 | 866 | //===----------------------------------------------------------------------===//
|
727 | 867 | // Definition of MallocBugVisitor.
|
728 | 868 | //===----------------------------------------------------------------------===//
|
729 | 869 |
|
| 870 | +namespace { |
730 | 871 | /// The bug visitor which allows us to print extra diagnostics along the
|
731 | 872 | /// BugReport path. For example, showing the allocation site of the leaked
|
732 | 873 | /// region.
|
@@ -851,7 +992,6 @@ class MallocBugVisitor final : public BugReporterVisitor {
|
851 | 992 | }
|
852 | 993 | };
|
853 | 994 | };
|
854 |
| - |
855 | 995 | } // end anonymous namespace
|
856 | 996 |
|
857 | 997 | // A map from the freed symbol to the symbol representing the return value of
|
@@ -2579,6 +2719,8 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
|
2579 | 2719 | AllocNode->getLocationContext()->getDecl());
|
2580 | 2720 | R->markInteresting(Sym);
|
2581 | 2721 | R->addVisitor<MallocBugVisitor>(Sym, true);
|
| 2722 | + if (ShouldRegisterNoOwnershipChangeVisitor) |
| 2723 | + R->addVisitor<NoOwnershipChangeVisitor>(Sym); |
2582 | 2724 | C.emitReport(std::move(R));
|
2583 | 2725 | }
|
2584 | 2726 |
|
@@ -3395,6 +3537,9 @@ void ento::registerDynamicMemoryModeling(CheckerManager &mgr) {
|
3395 | 3537 | auto *checker = mgr.registerChecker<MallocChecker>();
|
3396 | 3538 | checker->ShouldIncludeOwnershipAnnotatedFunctions =
|
3397 | 3539 | mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "Optimistic");
|
| 3540 | + checker->ShouldRegisterNoOwnershipChangeVisitor = |
| 3541 | + mgr.getAnalyzerOptions().getCheckerBooleanOption( |
| 3542 | + checker, "AddNoOwnershipChangeNotes"); |
3398 | 3543 | }
|
3399 | 3544 |
|
3400 | 3545 | bool ento::shouldRegisterDynamicMemoryModeling(const CheckerManager &mgr) {
|
|
0 commit comments