[clang] [analyzer] Only report the first dereference of the same variable in DanglingPtrDeref (PR #215409)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 10 15:03:03 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Benedek Kaibas (benedekaibas)
<details>
<summary>Changes</summary>
If the same variable is dereferenced multiple times then the `DanglingPtrDeref` checker should only emit a single wanring for it instead of multiple ones. If there are multiple variables dereferenced then for each variable there should be one warning emitted. For that reason I have implemented the `markAsReported` function which returns the updated state with the memory region of the given variable marked as reported if the memory region is seen for the first time.
---
Full diff: https://github.com/llvm/llvm-project/pull/215409.diff
4 Files Affected:
- (modified) clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp (+6)
- (modified) clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp (+8)
- (modified) clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h (+5)
- (modified) clang/test/Analysis/dangling-ptr-deref.cpp (+13)
``````````diff
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..18f3014cfa6e9 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 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
#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;
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/215409
More information about the cfe-commits
mailing list