| #include "LifetimeModeling.h" | |
| #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" | |
| #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" | |
| #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" | |
| #include "clang/StaticAnalyzer/Core/Checker.h" | |
| #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | |
| #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | |
| using namespace clang; | |
| using namespace ento; | |
| namespace { | |
| class DanglingPtrDeref : public Checker<check::Location, check::PostCall> { | |
| public: | |
| void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, | |
| CheckerContext &C) const; | |
| void checkPostCall(const CallEvent &Call, CheckerContext &C) const; | |
| void reportUseAfterScope(const MemRegion *Region, const Stmt *S, | |
| ExplodedNode *N, CheckerContext &C) const; | |
| const BugType BugMsg{this, "ReportDanglingPtrDeref", "LifetimeBound"}; | |
| }; | |
| class DanglingPtrDerefBRVisitor : public BugReporterVisitor { | |
| const MemRegion *SourceRegion; | |
| public: | |
| explicit DanglingPtrDerefBRVisitor(const MemRegion *Source) | |
| : SourceRegion(Source) {} | |
| void Profile(llvm::FoldingSetNodeID &ID) const override { | |
| ID.AddPointer(SourceRegion); | |
| } | |
| PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, | |
| BugReporterContext &BRC, | |
| PathSensitiveBugReport &BR) override; | |
| }; | |
| } // namespace | |
| void DanglingPtrDeref::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, | |
| CheckerContext &C) const { | |
| ProgramStateRef State = C.getState(); | |
| if (const MemRegion *LocRegion = Loc.getAsRegion()) { | |
| if (lifetime_modeling::isDeallocated(State, LocRegion)) { | |
| if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) | |
| reportUseAfterScope(LocRegion, S, N, C); | |
| } | |
| } | |
| } | |
| void DanglingPtrDeref::checkPostCall(const CallEvent &Call, | |
| CheckerContext &C) const { | |
| ProgramStateRef State = C.getState(); | |
| // Only check calls arguments if it is not inlined by the engine. In case a | |
| // function is inlined checkLocation handles any dereference in its body. | |
| if (C.wasInlined) | |
| return; | |
| for (unsigned Idx = 0; Idx < Call.getNumArgs(); Idx++) { | |
| if (const MemRegion *ArgRegion = Call.getArgSVal(Idx).getAsRegion()) | |
| if (lifetime_modeling::isDeallocated(State, ArgRegion)) | |
| if (ExplodedNode *N = C.generateNonFatalErrorNode()) | |
| reportUseAfterScope(ArgRegion, Call.getArgExpr(Idx), N, C); | |
| } | |
| } | |
| void DanglingPtrDeref::reportUseAfterScope(const MemRegion *Region, | |
| const Stmt *S, ExplodedNode *N, | |
| CheckerContext &C) const { | |
| ProgramStateRef ReportedState = | |
| lifetime_modeling::markAsReported(N->getState(), Region); | |
| if (!ReportedState) | |
| return; | |
| auto BR = std::make_unique<PathSensitiveBugReport>( | |
| BugMsg, | |
| (llvm::Twine("Use of ") + lifetime_modeling::getRegionName(Region) + | |
| " after its lifetime ended."), | |
| N); | |
| BR->addVisitor<DanglingPtrDerefBRVisitor>(Region); | |
| if (S) { | |
| if (const Expr *DerefExpr = bugreporter::getDerefExpr(S)) | |
| bugreporter::trackExpressionValue(N, DerefExpr, *BR); | |
| } | |
| C.addTransition(ReportedState, N); | |
| C.emitReport(std::move(BR)); | |
| } | |
| PathDiagnosticPieceRef | |
| DanglingPtrDerefBRVisitor::VisitNode(const ExplodedNode *N, | |
| BugReporterContext &BRC, | |
| PathSensitiveBugReport &BR) { | |
| using lifetime_modeling::isDeallocated; | |
| const ExplodedNode *Pred = N->getFirstPred(); | |
| if (!Pred) | |
| return nullptr; | |
| if (!isDeallocated(N->getState(), SourceRegion) || | |
| isDeallocated(Pred->getState(), SourceRegion)) | |
| return nullptr; | |
| const Stmt *S = N->getStmtForDiagnostics(); | |
| if (!S) | |
| return nullptr; | |
| PathDiagnosticLocation Pos = PathDiagnosticLocation::createEnd( | |
| S, BRC.getSourceManager(), N->getStackFrame()); | |
| return std::make_shared<PathDiagnosticEventPiece>( | |
| Pos, | |
| (lifetime_modeling::getRegionName(SourceRegion) + | |
| llvm::Twine(" is destroyed here")) | |
| .str(), | |
| true); | |
| } | |
| void ento::registerDanglingPtrDeref(CheckerManager &Mgr) { | |
| Mgr.registerChecker<DanglingPtrDeref>(); | |
| } | |
| bool ento::shouldRegisterDanglingPtrDeref(const CheckerManager &Mgr) { | |
| return true; | |
| } |