[clang] [analyzer] Only report the first dereference of the same variable in DanglingPtrDeref (PR #215409)
Benedek Kaibas via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 10 14:50:41 PDT 2026
https://github.com/benedekaibas updated https://github.com/llvm/llvm-project/pull/215409
>From c2cd1ff1c6cee173fd9381a353b288fc77481aaa Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Mon, 10 Aug 2026 23:43:34 +0200
Subject: [PATCH 1/2] [analyzer] Only report the first dereference of the same
variable in DanglingPtrDeref
---
.../StaticAnalyzer/Checkers/DanglingPtrDeref.cpp | 6 ++++++
.../StaticAnalyzer/Checkers/LifetimeModeling.cpp | 8 ++++++++
.../lib/StaticAnalyzer/Checkers/LifetimeModeling.h | 5 +++++
clang/test/Analysis/dangling-ptr-deref.cpp | 13 +++++++++++++
4 files changed, 32 insertions(+)
diff --git a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
index bd4cd864cb768..cd690328ebc47 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
@@ -69,6 +69,11 @@ void DanglingPtrDeref::checkPostCall(const CallEvent &Call,
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) +
@@ -79,6 +84,7 @@ void DanglingPtrDeref::reportUseAfterScope(const MemRegion *Region,
if (const Expr *DerefExpr = bugreporter::getDerefExpr(S))
bugreporter::trackExpressionValue(N, DerefExpr, *BR);
}
+ C.addTransition(ReportedState, N);
C.emitReport(std::move(BR));
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
index 2fab20b199f01..d4a3cfd060aa3 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
@@ -15,6 +15,7 @@ REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(LifetimeSourceSet, const MemRegion *)
REGISTER_MAP_WITH_PROGRAMSTATE(LifetimeBoundMap, SVal, LifetimeSourceSet)
REGISTER_SET_WITH_PROGRAMSTATE(DeallocatedSourceSet, const MemRegion *)
+REGISTER_SET_WITH_PROGRAMSTATE(ReportedDeadRegions, const MemRegion *)
namespace {
@@ -86,6 +87,13 @@ bool lifetime_modeling::isDeallocated(ProgramStateRef State,
return State->contains<DeallocatedSourceSet>(Region->getBaseRegion());
}
+ProgramStateRef lifetime_modeling::markAsReported(ProgramStateRef State,
+ const MemRegion *Region) {
+ if (State->contains<ReportedDeadRegions>(Region->getBaseRegion()))
+ return nullptr;
+ return State->add<ReportedDeadRegions>(Region->getBaseRegion());
+}
+
static ProgramStateRef bindSource(ProgramStateRef State, SVal RetVal,
const MemRegion *Source) {
LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
index 8d6c8e4882d1c..8cfe4526d0d51 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
@@ -22,6 +22,11 @@ bool isBoundToLifetimeSource(ProgramStateRef State, SVal Val);
/// Returns the descriptive name of the memory region or a placeholder if a
/// descriptive name cannot be constructed for it.
std::string getRegionName(const MemRegion *Reg);
+
+/// Returns true if \p R is seen the first time. If R was alreay reported
+/// before returns false.
+ProgramStateRef markAsReported(ProgramStateRef State, const MemRegion *Region);
+
} // namespace clang::ento::lifetime_modeling
#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_LIFETIMEMODELING_H
diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp b/clang/test/Analysis/dangling-ptr-deref.cpp
index 7f13c241dadf0..58f4aa7fa7583 100644
--- a/clang/test/Analysis/dangling-ptr-deref.cpp
+++ b/clang/test/Analysis/dangling-ptr-deref.cpp
@@ -331,3 +331,16 @@ void dangling_through_calls() {
// expected-warning at -1 {{Use of 'local' after its lifetime ended}}
// expected-note at -2 {{Use of 'local' after its lifetime ended}}
}
+
+// If the same variable is dereferenced multiple times then only
+// report for the first dereference.
+void multiple_deref() {
+ int *ptr = nullptr;
+ {
+ int a = 5; // expected-note {{'a' initialized to 5}}
+ ptr = &a; // expected-note {{Value assigned to 'ptr'}}
+ } // expected-note {{'a' is destroyed here}}
+ *ptr = 6; // expected-note {{Use of 'a' after its lifetime ended}}
+ // expected-warning at -1 {{Use of 'a' after its lifetime ended}}
+ *ptr = 7;
+}
>From 71cc3708e2858247327d0095384a7e503d427de7 Mon Sep 17 00:00:00 2001
From: benedekaibas <kaibas01 at allegheny.edu>
Date: Mon, 10 Aug 2026 23:50:19 +0200
Subject: [PATCH 2/2] Update doc comment for markAsReported.
---
clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
index 8cfe4526d0d51..18f3014cfa6e9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
+++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
@@ -23,8 +23,8 @@ bool isBoundToLifetimeSource(ProgramStateRef State, SVal Val);
/// descriptive name cannot be constructed for it.
std::string getRegionName(const MemRegion *Reg);
-/// Returns true if \p R is seen the first time. If R was alreay reported
-/// before returns false.
+/// Returns the updated \p State with \p R marked as reported if \p R is seen
+/// the first time. Returns nullptr if \p R was already reported.
ProgramStateRef markAsReported(ProgramStateRef State, const MemRegion *Region);
} // namespace clang::ento::lifetime_modeling
More information about the cfe-commits
mailing list