[clang] 17460a5 - [analyzer] Only report the first dereference of the same variable in DanglingPtrDeref (#215409)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 14 08:02:31 PDT 2026
Author: Benedek Kaibas
Date: 2026-08-14T17:02:23+02:00
New Revision: 17460a5bad231004a5747aecae589abda06c537d
URL: https://github.com/llvm/llvm-project/commit/17460a5bad231004a5747aecae589abda06c537d
DIFF: https://github.com/llvm/llvm-project/commit/17460a5bad231004a5747aecae589abda06c537d.diff
LOG: [analyzer] Only report the first dereference of the same variable in DanglingPtrDeref (#215409)
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.
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp
clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp
clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.h
clang/test/Analysis/dangling-ptr-deref.cpp
Removed:
################################################################################
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..7074e715d789b 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,14 @@ bool lifetime_modeling::isDeallocated(ProgramStateRef State,
return State->contains<DeallocatedSourceSet>(Region->getBaseRegion());
}
+ProgramStateRef lifetime_modeling::markAsReported(ProgramStateRef State,
+ const MemRegion *Region) {
+ ProgramStateRef NewState =
+ State->add<ReportedDeadRegions>(Region->getBaseRegion());
+
+ return (NewState != State) ? NewState : nullptr;
+}
+
static ProgramStateRef bindSource(ProgramStateRef State, SVal RetVal,
const MemRegion *Source) {
LifetimeSourceSet::Factory &F = State->get_context<LifetimeSourceSet>();
@@ -165,6 +174,7 @@ void LifetimeModeling::checkDeadSymbols(SymbolReaper &SymReaper,
ProgramStateRef State = C.getState();
LifetimeBoundMapTy LBMap = State->get<LifetimeBoundMap>();
DeallocatedSourceSetTy Sources = State->get<DeallocatedSourceSet>();
+ ReportedDeadRegionsTy Reported = State->get<ReportedDeadRegions>();
for (SVal Val : llvm::make_first_range(LBMap)) {
if (const auto *R = Val.getAsRegion(); R && SymReaper.isLiveRegion(R))
@@ -181,20 +191,32 @@ void LifetimeModeling::checkDeadSymbols(SymbolReaper &SymReaper,
if (!SymReaper.isLiveRegion(Region))
State = State->remove<DeallocatedSourceSet>(Region);
}
+
+ for (const MemRegion *Region : Reported) {
+ if (!SymReaper.isLiveRegion(Region))
+ State = State->remove<ReportedDeadRegions>(Region);
+ }
C.addTransition(State);
}
void LifetimeModeling::printState(raw_ostream &Out, ProgramStateRef State,
const char *NL, const char *Sep) const {
auto LBMap = State->get<LifetimeBoundMap>();
+ ReportedDeadRegionsTy Reported = State->get<ReportedDeadRegions>();
- if (LBMap.isEmpty())
- return;
+ if (!LBMap.isEmpty()) {
+ Out << Sep << "LifetimeBound bindings:" << NL;
+ for (auto &&[OriginSym, SourceSet] : LBMap) {
+ for (const auto *Region : SourceSet)
+ Out << " Origin " << OriginSym << " contains Loan " << Region << NL;
+ }
+ }
- Out << Sep << "LifetimeBound bindings:" << NL;
- for (auto &&[OriginSym, SourceSet] : LBMap) {
- for (const auto *Region : SourceSet)
- Out << " Origin " << OriginSym << " contains Loan " << Region << NL;
+ if (!Reported.isEmpty()) {
+ Out << Sep << "Reported regions: " << NL;
+ for (const auto *Region : Reported) {
+ Out << " " << Region << NL;
+ }
}
}
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..55dd5eadc8ad0 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; // no-warning: Already reported this base region.
+}
More information about the cfe-commits
mailing list