https://github.com/benedekaibas updated https://github.com/llvm/llvm-project/pull/206460
>From d1c7eb6cfd432345ee492fd3d25d14a02305d4e1 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Mon, 29 Jun 2026 10:19:38 +0200 Subject: [PATCH 1/7] Start implementing the reportDanglingPtrDeref checker. --- .../include/clang/StaticAnalyzer/Checkers/Checkers.td | 11 +++++++++++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt | 1 + 2 files changed, 12 insertions(+) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td index b565481d28fdb..631a273bb4f18 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -788,9 +788,20 @@ def SmartPtrChecker: Checker<"SmartPtr">, Dependencies<[SmartPtrModeling]>, Documentation<HasDocumentation>; +def LifetimeModeling : Checker<"LifetimeModeling">, + HelpText<"Model [[clang::lifetimebound]] annotations for lifetime analysis">, + Documentation<NotDocumented>, + Hidden; + def UseAfterLifetimeEnd : Checker<"UseAfterLifetimeEnd">, HelpText<"Check for uses of references or pointers that " "outlive their bound object">, + Dependencies<[LifetimeModeling]>, + Documentation<NotDocumented>; + +def ReportDanglingPtrDeref : Checker<"ReportDanglingPtrDeref">, + HelpText<"Check for dereferences of a dangling pointer">, + Dependencies<[LifetimeModeling]>, Documentation<NotDocumented>; } // end: "alpha.cplusplus" diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 46c0c36fda736..b05a51c46485c 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -92,6 +92,7 @@ add_clang_library(clangStaticAnalyzerCheckers PointerSubChecker.cpp PthreadLockChecker.cpp PutenvStackArrayChecker.cpp + ReportDanglingPtrDeref.cpp RetainCountChecker/RetainCountChecker.cpp RetainCountChecker/RetainCountDiagnostics.cpp ReturnPointerRangeChecker.cpp >From 5e7f0fb5a14052b220fe75106aba33eb5f64bd24 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Fri, 26 Jun 2026 01:09:44 +0200 Subject: [PATCH 2/7] Implemented the Modeling checker. --- .../Checkers/LifetimeModeling.h | 18 ++++ .../StaticAnalyzer/Checkers/CMakeLists.txt | 1 + .../Checkers/LifetimeModeling.cpp | 101 ++++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h create mode 100644 clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp diff --git a/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h b/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h new file mode 100644 index 0000000000000..671d5ce1ec5da --- /dev/null +++ b/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h @@ -0,0 +1,18 @@ +#ifndef LLVM_CLANG_INCLUDE_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H +#define LLVM_CLANG_INCLUDE_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include <vector> + +namespace clang { +namespace ento { +namespace lifetimemodeling { + +std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef, SVal); + +} // namespace lifetimemodeling +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_INCLUDE_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index b05a51c46485c..4ec6e368c8fc3 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -55,6 +55,7 @@ add_clang_library(clangStaticAnalyzerCheckers IteratorModeling.cpp IteratorRangeChecker.cpp IvarInvalidationChecker.cpp + LifetimeModeling.cpp LLVMConventionsChecker.cpp LocalizationChecker.cpp MacOSKeychainAPIChecker.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp new file mode 100644 index 0000000000000..65aa071a3284a --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp @@ -0,0 +1,101 @@ +#include "clang/StaticAnalyzer/Checkers/LifetimeModeling.h" +#include "clang/AST/Attr.h" +#include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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; +using namespace lifetimemodeling; + +REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *) +REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet) + +REGISTER_SET_WITH_PROGRAMSTATE(DeallocatedSourceSet, const MemRegion *) + +namespace { + +class LifetimeModeling : public Checker<check::PostCall, check::LifetimeEnd> { +public: + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkLifetimeEnd(const VarDecl *VD, CheckerContext &C) const; +}; + +} // namespace + +std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef State, + SVal Val) { + std::vector<const MemRegion *> StoreRegion; + if (const auto *SourceSet = State->get<LifetimeBoundMap>(Val)) { + for (const MemRegion *Region : *SourceSet) + StoreRegion.push_back(Region); + return StoreRegion; + } + return StoreRegion; +} + +static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal, + const MemRegion *Source) { + LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>(); + const LifetimeSourceSet *LSet = State->get<LifetimeBoundMap>(RetVal); + + LifetimeSourceSet Set = LSet ? *LSet : F.getEmptySet(); + Set = F.add(Set, Source); + State = State->set<LifetimeBoundMap>(RetVal, Set); + return State; +} + +void LifetimeModeling::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + const auto *FC = dyn_cast<AnyFunctionCall>(&Call); + if (!FC) + return; + + const FunctionDecl *FD = FC->getDecl(); + if (!FD) + return; + + SVal RetVal = Call.getReturnValue(); + + for (const ParmVarDecl *PVD : FD->parameters()) { + if (PVD->hasAttr<LifetimeBoundAttr>()) { + unsigned Idx = PVD->getFunctionScopeIndex(); + SVal Arg = Call.getArgSVal(Idx); + if (const MemRegion *ArgValRegion = Arg.getAsRegion()) + State = bindValues(State, RetVal, ArgValRegion); + } + } + + if (const auto *IC = dyn_cast<CXXInstanceCall>(&Call)) { + if (lifetimes::implicitObjectParamIsLifetimeBound(FD)) { + if (const MemRegion *AttrRegion = IC->getCXXThisVal().getAsRegion()) + State = bindValues(State, RetVal, AttrRegion); + } + } + C.addTransition(State); +} + +void LifetimeModeling::checkLifetimeEnd(const VarDecl *VD, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (!VD) + return; + + SVal SourceVal = State->getLValue(VD, C.getStackFrame()); + if (const MemRegion *SourceValRegion = SourceVal.getAsRegion()) { + State = State->add<DeallocatedSourceSet>(SourceValRegion); + C.addTransition(State); + } +} + +void ento::registerLifetimeModeling(CheckerManager &Mgr) { + Mgr.registerChecker<LifetimeModeling>(); +} + +bool ento::shouldRegisterLifetimeModeling(const CheckerManager &Mgr) { + return true; +} >From 187fcbf113977244fb14ec79ccc81c9893f44073 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Mon, 29 Jun 2026 10:54:21 +0200 Subject: [PATCH 3/7] Add isDeallocated API to the modeling checker. --- .../include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h | 1 + clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h b/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h index 671d5ce1ec5da..e9eb4979e05d2 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h +++ b/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h @@ -10,6 +10,7 @@ namespace ento { namespace lifetimemodeling { std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef, SVal); +bool isDeallocated(ProgramStateRef, const MemRegion *); } // namespace lifetimemodeling } // namespace ento diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp index 65aa071a3284a..1355c4218f921 100644 --- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp @@ -31,11 +31,14 @@ std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef State, if (const auto *SourceSet = State->get<LifetimeBoundMap>(Val)) { for (const MemRegion *Region : *SourceSet) StoreRegion.push_back(Region); - return StoreRegion; } return StoreRegion; } +bool isDeallocated(ProgramStateRef State, const MemRegion *Region) { + return State->contains<DeallocatedSourceSet>(Region); +} + static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal, const MemRegion *Source) { LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>(); >From 98aaa83b3029da553dfe751488597e97367c17ea Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Mon, 29 Jun 2026 12:57:19 +0200 Subject: [PATCH 4/7] Added ReportDanglingPtrDeref checker. --- .../Checkers/LifetimeModeling.cpp | 10 +++- .../Checkers/ReportDanglingPtrDeref.cpp | 46 +++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp index 1355c4218f921..ea9949d9696ba 100644 --- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp @@ -8,7 +8,6 @@ using namespace clang; using namespace ento; -using namespace lifetimemodeling; REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *) REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet) @@ -24,9 +23,12 @@ class LifetimeModeling : public Checker<check::PostCall, check::LifetimeEnd> { }; } // namespace - +namespace clang { +namespace ento { +namespace lifetimemodeling { std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef State, SVal Val) { + std::vector<const MemRegion *> StoreRegion; if (const auto *SourceSet = State->get<LifetimeBoundMap>(Val)) { for (const MemRegion *Region : *SourceSet) @@ -39,6 +41,10 @@ bool isDeallocated(ProgramStateRef State, const MemRegion *Region) { return State->contains<DeallocatedSourceSet>(Region); } +} // namespace lifetimemodeling +} // namespace ento +} // namespace clang + static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal, const MemRegion *Source) { LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>(); diff --git a/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp b/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp new file mode 100644 index 0000000000000..24620d369b80d --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp @@ -0,0 +1,46 @@ +#include "clang/StaticAnalyzer/Checkers/LifetimeModeling.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" + +using namespace clang; +using namespace ento; + +namespace { +class ReportDanglingPtrDeref : public Checker<check::Location> { +public: + void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const; + void reportUseAfterScope(const MemRegion *Region, ExplodedNode *N, CheckerContext &C) const; + const BugType BugMsg{this, "ReportDanglingPtrDeref", "LifetimeBound"}; +}; +} // namespace + +void ReportDanglingPtrDeref::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + if (const MemRegion *LocRegion = Loc.getAsRegion()) { + if (lifetimemodeling::isDeallocated(State, LocRegion)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) + reportUseAfterScope(LocRegion, N, C); + } + } +} + +void ReportDanglingPtrDeref::reportUseAfterScope(const MemRegion *Region, ExplodedNode *N, CheckerContext &C) const { + auto BR = std::make_unique<PathSensitiveBugReport>( + BugMsg, + (llvm::Twine("Use of '") + Region->getString() + + "' after its lifetime ended.") + .str(), + N); + C.emitReport(std::move(BR)); +} + +void ento::registerReportDanglingPtrDeref(CheckerManager &Mgr) { + Mgr.registerChecker<ReportDanglingPtrDeref>(); +} + +bool ento::shouldRegisterReportDanglingPtrDeref(const CheckerManager &Mgr) { + return true; +} >From 3c6bce8714d946417a0832a57cd36185981b9228 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Mon, 29 Jun 2026 17:47:21 +0200 Subject: [PATCH 5/7] Implemented reportDanglingPtrDeref checker and added test suites for the checker. --- .../Checkers/LifetimeModeling.cpp | 2 +- .../Checkers/ReportDanglingPtrDeref.cpp | 17 ++++--- clang/test/Analysis/dangling-ptr-deref.cpp | 45 +++++++++++++++++++ 3 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 clang/test/Analysis/dangling-ptr-deref.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp index ea9949d9696ba..ab99dcd2ba851 100644 --- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp @@ -23,12 +23,12 @@ class LifetimeModeling : public Checker<check::PostCall, check::LifetimeEnd> { }; } // namespace + namespace clang { namespace ento { namespace lifetimemodeling { std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef State, SVal Val) { - std::vector<const MemRegion *> StoreRegion; if (const auto *SourceSet = State->get<LifetimeBoundMap>(Val)) { for (const MemRegion *Region : *SourceSet) diff --git a/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp b/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp index 24620d369b80d..309468b1736f6 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp @@ -1,8 +1,8 @@ -#include "clang/StaticAnalyzer/Checkers/LifetimeModeling.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Checkers/LifetimeModeling.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" using namespace clang; using namespace ento; @@ -10,13 +10,16 @@ using namespace ento; namespace { class ReportDanglingPtrDeref : public Checker<check::Location> { public: - void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const; - void reportUseAfterScope(const MemRegion *Region, ExplodedNode *N, CheckerContext &C) const; + void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &C) const; + void reportUseAfterScope(const MemRegion *Region, ExplodedNode *N, + CheckerContext &C) const; const BugType BugMsg{this, "ReportDanglingPtrDeref", "LifetimeBound"}; }; } // namespace -void ReportDanglingPtrDeref::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const { +void ReportDanglingPtrDeref::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &C) const { ProgramStateRef State = C.getState(); if (const MemRegion *LocRegion = Loc.getAsRegion()) { @@ -27,7 +30,9 @@ void ReportDanglingPtrDeref::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, } } -void ReportDanglingPtrDeref::reportUseAfterScope(const MemRegion *Region, ExplodedNode *N, CheckerContext &C) const { +void ReportDanglingPtrDeref::reportUseAfterScope(const MemRegion *Region, + ExplodedNode *N, + CheckerContext &C) const { auto BR = std::make_unique<PathSensitiveBugReport>( BugMsg, (llvm::Twine("Use of '") + Region->getString() + diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp b/clang/test/Analysis/dangling-ptr-deref.cpp new file mode 100644 index 0000000000000..05755f09c0e57 --- /dev/null +++ b/clang/test/Analysis/dangling-ptr-deref.cpp @@ -0,0 +1,45 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.ReportDanglingPtrDeref \ +// RUN: -analyzer-config cfg-lifetime=true -verify %s +// RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.ReportDanglingPtrDeref \ +// RUN: -analyzer-config c++-container-inlining=false -analyzer-config cfg-lifetime=true -verify %s + +void test_case_one() { + int *ptr = nullptr; + { + int num = 5; + ptr = # + } + *ptr = 6; // expected-warning {{Use of 'num' after its lifetime ended}} +} + +void test_case_two() { + int *ptr_one = nullptr; + int *ptr_two = nullptr; + { + int n = 1; + int m = 2; + ptr_one = &n; + ptr_two = &m; + } + *ptr_one = 6; // expected-warning {{Use of 'n' after its lifetime ended}} + *ptr_two = 7; // expected-warning {{Use of 'm' after its lifetime ended}} +} + +void test_case_three() { + int num = 5; + int *ptr = # + { + *ptr = 6; // no-warning + } +} + +void test_case_four() { + int *ptr = nullptr; + { + int num = 5; + ptr = # + } + int i = *ptr; // expected-warning {{Use of 'num' after its lifetime ended}} + i += i; +} + >From 04f31a393fe9750f2ebb5d6d8fc4739dded2a814 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Thu, 9 Jul 2026 12:59:16 +0200 Subject: [PATCH 6/7] [analyzer] Implemented VisitNode for BugReporterVisitor. --- .../Checkers/ReportDanglingPtrDeref.cpp | 49 ++++++++++++++++++- clang/test/Analysis/dangling-ptr-deref.cpp | 20 +++++--- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp b/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp index 309468b1736f6..52438dbd2a330 100644 --- a/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/ReportDanglingPtrDeref.cpp @@ -1,6 +1,7 @@ #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Checkers/LifetimeModeling.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/CheckerContext.h" @@ -10,12 +11,33 @@ using namespace ento; namespace { class ReportDanglingPtrDeref : public Checker<check::Location> { public: + class ReportDanglingPtrDerefBRVisitor : public BugReporterVisitor { + SVal BoundRegion; + const MemRegion *SourceRegion; + + public: + ReportDanglingPtrDerefBRVisitor(SVal Region, const MemRegion *Source) + : BoundRegion(Region), SourceRegion(Source) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + BoundRegion.Profile(ID); + SourceRegion->Profile(ID); + } + + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; + }; + void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const; void reportUseAfterScope(const MemRegion *Region, ExplodedNode *N, CheckerContext &C) const; const BugType BugMsg{this, "ReportDanglingPtrDeref", "LifetimeBound"}; }; + } // namespace void ReportDanglingPtrDeref::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, @@ -23,8 +45,9 @@ void ReportDanglingPtrDeref::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, ProgramStateRef State = C.getState(); if (const MemRegion *LocRegion = Loc.getAsRegion()) { - if (lifetimemodeling::isDeallocated(State, LocRegion)) { - if (ExplodedNode *N = C.generateNonFatalErrorNode()) + if (lifetime_modeling::isDeallocated(State, LocRegion)) { + if (ExplodedNode *N = + C.generateNonFatalErrorNode(C.getState(), C.getPredecessor())) reportUseAfterScope(LocRegion, N, C); } } @@ -42,6 +65,28 @@ void ReportDanglingPtrDeref::reportUseAfterScope(const MemRegion *Region, C.emitReport(std::move(BR)); } +PathDiagnosticPieceRef +ReportDanglingPtrDeref::ReportDanglingPtrDerefBRVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { + if (!lifetime_modeling::isBoundToLifetimeSourceSet(N->getState(), + BoundRegion) || + lifetime_modeling::isBoundToLifetimeSourceSet( + N->getFirstPred()->getState(), BoundRegion)) + return nullptr; + + const Stmt *S = N->getStmtForDiagnostics(); + if (!S) + return nullptr; + + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getStackFrame()); + return std::make_shared<PathDiagnosticEventPiece>( + Pos, + (llvm::Twine("'") + SourceRegion->getString() + "' is destroyed here") + .str(), + true); +} + void ento::registerReportDanglingPtrDeref(CheckerManager &Mgr) { Mgr.registerChecker<ReportDanglingPtrDeref>(); } diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp b/clang/test/Analysis/dangling-ptr-deref.cpp index 05755f09c0e57..2cbbadcd24313 100644 --- a/clang/test/Analysis/dangling-ptr-deref.cpp +++ b/clang/test/Analysis/dangling-ptr-deref.cpp @@ -1,7 +1,7 @@ // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.ReportDanglingPtrDeref \ -// RUN: -analyzer-config cfg-lifetime=true -verify %s +// RUN: -analyzer-config cfg-lifetime=true -analyzer-output=text -verify %s // RUN: %clang_analyze_cc1 -analyzer-checker=core,alpha.cplusplus.ReportDanglingPtrDeref \ -// RUN: -analyzer-config c++-container-inlining=false -analyzer-config cfg-lifetime=true -verify %s +// RUN: -analyzer-config c++-container-inlining=false -analyzer-config cfg-lifetime=true -analyzer-output=text -verify %s void test_case_one() { int *ptr = nullptr; @@ -9,7 +9,9 @@ void test_case_one() { int num = 5; ptr = # } - *ptr = 6; // expected-warning {{Use of 'num' after its lifetime ended}} + *ptr = 6; + // expected-warning@-1 {{Use of 'num' after its lifetime ended}} + // expected-note@-2 {{Use of 'num' after its lifetime ended}} } void test_case_two() { @@ -21,8 +23,12 @@ void test_case_two() { ptr_one = &n; ptr_two = &m; } - *ptr_one = 6; // expected-warning {{Use of 'n' after its lifetime ended}} - *ptr_two = 7; // expected-warning {{Use of 'm' after its lifetime ended}} + *ptr_one = 6; + *ptr_two = 7; + // expected-warning@-2 {{Use of 'n' after its lifetime ended}} + // expected-warning@-2 {{Use of 'm' after its lifetime ended}} + // expected-note@-4 {{Use of 'n' after its lifetime ended}} + // expected-note@-4 {{Use of 'm' after its lifetime ended}} } void test_case_three() { @@ -39,7 +45,9 @@ void test_case_four() { int num = 5; ptr = # } - int i = *ptr; // expected-warning {{Use of 'num' after its lifetime ended}} + int i = *ptr; + // expected-warning@-1 {{Use of 'num' after its lifetime ended}} + // expected-note@-2 {{Use of 'num' after its lifetime ended}} i += i; } >From aedc5859fbd80d469f13eb1be3237836d375fcef Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Thu, 9 Jul 2026 13:11:43 +0200 Subject: [PATCH 7/7] [analyzer] Updated the modeling checker. --- .../StaticAnalyzer/Checkers/LifetimeModeling.h | 15 ++++++++------- .../StaticAnalyzer/Checkers/LifetimeModeling.cpp | 12 ++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h b/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h index e9eb4979e05d2..b39735c09d63a 100644 --- a/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h +++ b/clang/include/clang/StaticAnalyzer/Checkers/LifetimeModeling.h @@ -5,15 +5,16 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include <vector> -namespace clang { -namespace ento { -namespace lifetimemodeling { - +namespace clang::ento::lifetime_modeling { +// FIXME: Once #205951 lands this function might needs to be removed. std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef, SVal); + +/// Returns true if the SVal key is present in the map. +bool isBoundToLifetimeSourceSet(ProgramStateRef State, SVal Val); + +// FIXME: Once PR #205951 lands this function might needs to be removed. bool isDeallocated(ProgramStateRef, const MemRegion *); -} // namespace lifetimemodeling -} // namespace ento -} // namespace clang +} // namespace clang::ento::lifetime_modeling #endif // LLVM_CLANG_INCLUDE_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp index ab99dcd2ba851..d7e881f29fb97 100644 --- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp @@ -24,9 +24,11 @@ class LifetimeModeling : public Checker<check::PostCall, check::LifetimeEnd> { } // namespace -namespace clang { -namespace ento { -namespace lifetimemodeling { +namespace clang::ento::lifetime_modeling { +bool isBoundToLifetimeSourceSet(ProgramStateRef State, SVal Val) { + return State->get<LifetimeBoundMap>(Val) != nullptr; +} + std::vector<const MemRegion *> getLifetimeSourceSet(ProgramStateRef State, SVal Val) { std::vector<const MemRegion *> StoreRegion; @@ -41,9 +43,7 @@ bool isDeallocated(ProgramStateRef State, const MemRegion *Region) { return State->contains<DeallocatedSourceSet>(Region); } -} // namespace lifetimemodeling -} // namespace ento -} // namespace clang +} // namespace clang::ento::lifetime_modeling static ProgramStateRef bindValues(ProgramStateRef State, SVal RetVal, const MemRegion *Source) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
