https://github.com/benedekaibas created https://github.com/llvm/llvm-project/pull/211552
Currently the `DanglingPtrDeref` checker only records the whole object's region as deallocated. However, dangling pointers often refer to subobjects (e.g., a struct field or an element of an array). With the change in this PR, `isDeallocated` now checks whether a region's base region is in the `DeallocatedSourceSet`. >From 28b8e2424a5e3c947765457f2de5f2bf937d100b Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Thu, 23 Jul 2026 15:01:21 +0200 Subject: [PATCH] [analyzer] Match dangling subobjects by their base region in DanglingPtrDeref --- .../Checkers/DanglingPtrDeref.cpp | 14 +++-- .../Checkers/LifetimeModeling.cpp | 2 +- clang/test/Analysis/dangling-ptr-deref.cpp | 53 +++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp index ff2087e1db933..41fcc68f8f109 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp @@ -66,13 +66,21 @@ void DanglingPtrDeref::checkPostCall(const CallEvent &Call, } } +static std::string getRegionName(const MemRegion *Reg) { + // FIXME: Once the checker supports heap allocation, more region kinds + // should be handled to produce the correct descriptive name. + if (const std::string RegName = Reg->getDescriptiveName(); !RegName.empty()) + return RegName; + return "the region"; +} + void DanglingPtrDeref::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."), + (llvm::Twine("Use of ") + getRegionName(Region) + + " after its lifetime ended."), N); BR->addVisitor<DanglingPtrDerefBRVisitor>(Region); C.emitReport(std::move(BR)); @@ -99,7 +107,7 @@ DanglingPtrDerefBRVisitor::VisitNode(const ExplodedNode *N, S, BRC.getSourceManager(), N->getStackFrame()); return std::make_shared<PathDiagnosticEventPiece>( Pos, - (llvm::Twine("'") + SourceRegion->getString() + "' is destroyed here") + (llvm::Twine() + getRegionName(SourceRegion) + " is destroyed here") .str(), true); } diff --git a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp index 2b6f5dae4243f..ef0b1cb264b18 100644 --- a/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/LifetimeModeling.cpp @@ -73,7 +73,7 @@ std::vector<const MemRegion *> lifetime_modeling::getDanglingRegionsAfterReturn( bool lifetime_modeling::isDeallocated(ProgramStateRef State, const MemRegion *Region) { - return State->contains<DeallocatedSourceSet>(Region); + return State->contains<DeallocatedSourceSet>(Region->getBaseRegion()); } static ProgramStateRef bindSource(ProgramStateRef State, SVal RetVal, diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp b/clang/test/Analysis/dangling-ptr-deref.cpp index e661eff9ccbb6..9deda4b2502a4 100644 --- a/clang/test/Analysis/dangling-ptr-deref.cpp +++ b/clang/test/Analysis/dangling-ptr-deref.cpp @@ -112,3 +112,56 @@ void inlined_callee_single_report() { // expected-note@-1 {{Calling 'deref_param'}} (void)r; } + +struct MyBuffer { + char buffer[8]; +}; + +void member_subregion_dangling_deref() { + const char *p = nullptr; + { + struct MyBuffer tmp_buffer = {}; + p = tmp_buffer.buffer; + } + // expected-note@-1 {{'tmp_buffer.buffer[0]' is destroyed here}} + char c = *p; + // expected-warning@-1 {{Use of 'tmp_buffer.buffer[0]' after its lifetime ended}} + // expected-note@-2 {{Use of 'tmp_buffer.buffer[0]' after its lifetime ended}} + (void)c; +} + +void opaque(const char *); + +void passing_dangling_to_call() { + const char *p = nullptr; + { + struct MyBuffer tmp_buffer = {}; + p = tmp_buffer.buffer; + } + // expected-note@-1 {{'tmp_buffer.buffer[0]' is destroyed here}} + opaque(p); + // expected-warning@-1 {{Use of 'tmp_buffer.buffer[0]' after its lifetime ended}} + // expected-note@-2 {{Use of 'tmp_buffer.buffer[0]' after its lifetime ended}} +} + +void member_subregion_alive_deref() { + { + struct MyBuffer tmp_buffer = {}; + const char *p = tmp_buffer.buffer; + opaque(p); // no-warning + char c = *p; // no-warning + (void)c; + } +} + +void arr_elem_subreg_dangling_deref() { + int *ptr = nullptr; + { + int local_arr[5]; + ptr = &local_arr[1]; + } + // expected-note@-1 {{'local_arr[1]' is destroyed here}} + *ptr = 7; + // expected-warning@-1 {{Use of 'local_arr[1]' after its lifetime ended}} + // expected-note@-2 {{Use of 'local_arr[1]' after its lifetime ended}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
