| 1 | #include "LifetimeModeling.h" |
| 2 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
| 3 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
| 4 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" |
| 5 | #include "clang/StaticAnalyzer/Core/Checker.h" |
| 6 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
| 7 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
| 8 | |
| 9 | using namespace clang; |
| 10 | using namespace ento; |
| 11 | |
| 12 | namespace { |
| 13 | class DanglingPtrDeref : public Checker<check::Location, check::PostCall> { |
| 14 | public: |
| 15 | void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, |
| 16 | CheckerContext &C) const; |
| 17 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
| 18 | void reportUseAfterScope(const MemRegion *Region, const Stmt *S, |
| 19 | ExplodedNode *N, CheckerContext &C) const; |
| 20 | const BugType BugMsg{this, "ReportDanglingPtrDeref" , "LifetimeBound" }; |
| 21 | }; |
| 22 | |
| 23 | class DanglingPtrDerefBRVisitor : public BugReporterVisitor { |
| 24 | const MemRegion *SourceRegion; |
| 25 | |
| 26 | public: |
| 27 | explicit DanglingPtrDerefBRVisitor(const MemRegion *Source) |
| 28 | : SourceRegion(Source) {} |
| 29 | |
| 30 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
| 31 | ID.AddPointer(Ptr: SourceRegion); |
| 32 | } |
| 33 | |
| 34 | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
| 35 | BugReporterContext &BRC, |
| 36 | PathSensitiveBugReport &BR) override; |
| 37 | }; |
| 38 | |
| 39 | } // namespace |
| 40 | |
| 41 | void DanglingPtrDeref::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, |
| 42 | CheckerContext &C) const { |
| 43 | ProgramStateRef State = C.getState(); |
| 44 | |
| 45 | if (const MemRegion *LocRegion = Loc.getAsRegion()) { |
| 46 | if (lifetime_modeling::isDeallocated(State, Region: LocRegion)) { |
| 47 | if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) |
| 48 | reportUseAfterScope(Region: LocRegion, S, N, C); |
| 49 | } |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | void DanglingPtrDeref::checkPostCall(const CallEvent &Call, |
| 54 | CheckerContext &C) const { |
| 55 | ProgramStateRef State = C.getState(); |
| 56 | // Only check calls arguments if it is not inlined by the engine. In case a |
| 57 | // function is inlined checkLocation handles any dereference in its body. |
| 58 | if (C.wasInlined) |
| 59 | return; |
| 60 | |
| 61 | for (unsigned Idx = 0; Idx < Call.getNumArgs(); Idx++) { |
| 62 | if (const MemRegion *ArgRegion = Call.getArgSVal(Index: Idx).getAsRegion()) |
| 63 | if (lifetime_modeling::isDeallocated(State, Region: ArgRegion)) |
| 64 | if (ExplodedNode *N = C.generateNonFatalErrorNode()) |
| 65 | reportUseAfterScope(Region: ArgRegion, S: Call.getArgExpr(Index: Idx), N, C); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | void DanglingPtrDeref::reportUseAfterScope(const MemRegion *Region, |
| 70 | const Stmt *S, ExplodedNode *N, |
| 71 | CheckerContext &C) const { |
| 72 | ProgramStateRef ReportedState = |
| 73 | lifetime_modeling::markAsReported(State: N->getState(), Region); |
| 74 | if (!ReportedState) |
| 75 | return; |
| 76 | |
| 77 | auto BR = std::make_unique<PathSensitiveBugReport>( |
| 78 | args: BugMsg, |
| 79 | args: (llvm::Twine("Use of " ) + lifetime_modeling::getRegionName(Reg: Region) + |
| 80 | " after its lifetime ended." ), |
| 81 | args&: N); |
| 82 | BR->addVisitor<DanglingPtrDerefBRVisitor>(ConstructorArgs&: Region); |
| 83 | if (S) { |
| 84 | if (const Expr *DerefExpr = bugreporter::getDerefExpr(S)) |
| 85 | bugreporter::trackExpressionValue(N, E: DerefExpr, R&: *BR); |
| 86 | } |
| 87 | C.addTransition(State: ReportedState, Pred: N); |
| 88 | C.emitReport(R: std::move(BR)); |
| 89 | } |
| 90 | |
| 91 | PathDiagnosticPieceRef |
| 92 | DanglingPtrDerefBRVisitor::VisitNode(const ExplodedNode *N, |
| 93 | BugReporterContext &BRC, |
| 94 | PathSensitiveBugReport &BR) { |
| 95 | using lifetime_modeling::isDeallocated; |
| 96 | const ExplodedNode *Pred = N->getFirstPred(); |
| 97 | if (!Pred) |
| 98 | return nullptr; |
| 99 | |
| 100 | if (!isDeallocated(State: N->getState(), Region: SourceRegion) || |
| 101 | isDeallocated(State: Pred->getState(), Region: SourceRegion)) |
| 102 | return nullptr; |
| 103 | |
| 104 | const Stmt *S = N->getStmtForDiagnostics(); |
| 105 | if (!S) |
| 106 | return nullptr; |
| 107 | |
| 108 | PathDiagnosticLocation Pos = PathDiagnosticLocation::createEnd( |
| 109 | S, SM: BRC.getSourceManager(), SFAC: N->getStackFrame()); |
| 110 | return std::make_shared<PathDiagnosticEventPiece>( |
| 111 | args&: Pos, |
| 112 | args: (lifetime_modeling::getRegionName(Reg: SourceRegion) + |
| 113 | llvm::Twine(" is destroyed here" )) |
| 114 | .str(), |
| 115 | args: true); |
| 116 | } |
| 117 | |
| 118 | void ento::registerDanglingPtrDeref(CheckerManager &Mgr) { |
| 119 | Mgr.registerChecker<DanglingPtrDeref>(); |
| 120 | } |
| 121 | |
| 122 | bool ento::shouldRegisterDanglingPtrDeref(const CheckerManager &Mgr) { |
| 123 | return true; |
| 124 | } |
| 125 | |