[clang] [analyzer] Implemented the DanglingPtrDeref checker to detect use-after-scope lifetime errors (PR #206460)
Benedek Kaibas via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 9 04:12:05 PDT 2026
https://github.com/benedekaibas updated https://github.com/llvm/llvm-project/pull/206460
>From d1c7eb6cfd432345ee492fd3d25d14a02305d4e1 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas.benedek02 at gmail.com>
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 <kaibas.benedek02 at gmail.com>
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 <kaibas.benedek02 at gmail.com>
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 <kaibas.benedek02 at gmail.com>
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 <kaibas.benedek02 at gmail.com>
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 <kaibas01 at allegheny.edu>
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 at -1 {{Use of 'num' after its lifetime ended}}
+ // expected-note at -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 at -2 {{Use of 'n' after its lifetime ended}}
+ // expected-warning at -2 {{Use of 'm' after its lifetime ended}}
+ // expected-note at -4 {{Use of 'n' after its lifetime ended}}
+ // expected-note at -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 at -1 {{Use of 'num' after its lifetime ended}}
+ // expected-note at -2 {{Use of 'num' after its lifetime ended}}
i += i;
}
>From aedc5859fbd80d469f13eb1be3237836d375fcef Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
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) {
More information about the cfe-commits
mailing list