https://github.com/benedekaibas updated https://github.com/llvm/llvm-project/pull/211552
>From 28b8e2424a5e3c947765457f2de5f2bf937d100b Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Thu, 23 Jul 2026 15:01:21 +0200 Subject: [PATCH 1/2] [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}} +} >From 2cdca2d740aca62e6b947b6afffb22e1ba3b7f57 Mon Sep 17 00:00:00 2001 From: benedekaibas <[email protected]> Date: Thu, 23 Jul 2026 23:55:54 +0200 Subject: [PATCH 2/2] Resolve nits. --- .../Checkers/DanglingPtrDeref.cpp | 3 +- clang/test/Analysis/dangling-ptr-deref.cpp | 61 +++++++++++++++++-- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp index 41fcc68f8f109..d1b9f0fea4044 100644 --- a/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/DanglingPtrDeref.cpp @@ -107,8 +107,7 @@ DanglingPtrDerefBRVisitor::VisitNode(const ExplodedNode *N, S, BRC.getSourceManager(), N->getStackFrame()); return std::make_shared<PathDiagnosticEventPiece>( Pos, - (llvm::Twine() + getRegionName(SourceRegion) + " is destroyed here") - .str(), + (getRegionName(SourceRegion) + llvm::Twine(" is destroyed here")).str(), true); } diff --git a/clang/test/Analysis/dangling-ptr-deref.cpp b/clang/test/Analysis/dangling-ptr-deref.cpp index 9deda4b2502a4..654b23d0fea64 100644 --- a/clang/test/Analysis/dangling-ptr-deref.cpp +++ b/clang/test/Analysis/dangling-ptr-deref.cpp @@ -116,18 +116,20 @@ void inlined_callee_single_report() { struct MyBuffer { char buffer[8]; }; +struct MyStruct { int x; }; +struct Inner { int x; }; +struct Outer { struct Inner inner; }; -void member_subregion_dangling_deref() { +char 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; + return *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 *); @@ -144,13 +146,12 @@ void passing_dangling_to_call() { // expected-note@-2 {{Use of 'tmp_buffer.buffer[0]' after its lifetime ended}} } -void member_subregion_alive_deref() { +char 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; + return *p; // no-warning } } @@ -165,3 +166,51 @@ void arr_elem_subreg_dangling_deref() { // expected-warning@-1 {{Use of 'local_arr[1]' after its lifetime ended}} // expected-note@-2 {{Use of 'local_arr[1]' after its lifetime ended}} } + +char member_array_elem__dangling_deref() { + const char *p = nullptr; + { + struct MyBuffer tmp_buffer = {}; + p = tmp_buffer.buffer + 3; + } + // expected-note@-1 {{'tmp_buffer.buffer[3]' is destroyed here}} + return *p; + // expected-warning@-1 {{Use of 'tmp_buffer.buffer[3]' after its lifetime ended}} + // expected-note@-2 {{Use of 'tmp_buffer.buffer[3]' after its lifetime ended}} +} + +int struct_field_dangling_deref() { + int *p = nullptr; + { + struct MyStruct s = {}; + p = &s.x; + } + // expected-note@-1 {{'s.x' is destroyed here}} + return *p; + // expected-warning@-1 {{Use of 's.x' after its lifetime ended}} + // expected-note@-2 {{Use of 's.x' after its lifetime ended}} +} + +int struct_array_element_dangling_deref() { + int *p = nullptr; + { + struct MyStruct arr[4] = {}; + p = &arr[2].x; + } + // expected-note@-1 {{'arr[2].x' is destroyed here}} + return *p; + // expected-warning@-1 {{Use of 'arr[2].x' after its lifetime ended}} + // expected-note@-2 {{Use of 'arr[2].x' after its lifetime ended}} +} + +int nested_field_dangling_deref() { + int *p = nullptr; + { + struct Outer o = {}; + p = &o.inner.x; + } + // expected-note@-1 {{'o.inner.x' is destroyed here}} + return *p; + // expected-warning@-1 {{Use of 'o.inner.x' after its lifetime ended}} + // expected-note@-2 {{Use of 'o.inner.x' after its lifetime ended}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
