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
9using namespace clang;
10using namespace ento;
11
12namespace {
13class DanglingPtrDeref : public Checker<check::Location, check::PostCall> {
14public:
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
23class DanglingPtrDerefBRVisitor : public BugReporterVisitor {
24 const MemRegion *SourceRegion;
25
26public:
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
41void 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
53void 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
69void 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
91PathDiagnosticPieceRef
92DanglingPtrDerefBRVisitor::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
118void ento::registerDanglingPtrDeref(CheckerManager &Mgr) {
119 Mgr.registerChecker<DanglingPtrDeref>();
120}
121
122bool ento::shouldRegisterDanglingPtrDeref(const CheckerManager &Mgr) {
123 return true;
124}
125