https://github.com/voyager-jhk created https://github.com/llvm/llvm-project/pull/221587
Pointers stored in heap fields are incorrectly considered escaped, causing leaks to go unreported. Fixes #214226 >From a4998e0693f07b00ac7f40dcd4154f1ccb53e43e Mon Sep 17 00:00:00 2001 From: voyager-jhk <[email protected]> Date: Sun, 6 Sep 2026 23:52:43 +0800 Subject: [PATCH] [clang] Fix NewDeleteLeaks false negative for heap fields Pointers stored in heap fields are incorrectly considered escaped, causing leaks to go unreported. Fixes #214226 --- clang/lib/StaticAnalyzer/Core/ExprEngine.cpp | 6 +++-- clang/test/Analysis/NewDeleteLeaks.cpp | 23 ++++++++++++++++++++ clang/test/Analysis/malloc.c | 9 +++++++- 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp index f0f7d78fc5d50..69b091650c9f5 100644 --- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -3442,7 +3442,8 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, // A value escapes in four possible cases: // (1) We are binding to something that is not a memory region. -// (2) We are binding to a MemRegion that does not have stack storage. +// (2) We are binding to a MemRegion that is not in stack, static-local, +// or heap space. // (3) We are binding to a top-level parameter region with a non-trivial // destructor. We won't see the destructor during analysis, but it's there. // (4) We are binding to a MemRegion with stack storage that the store @@ -3455,7 +3456,8 @@ ProgramStateRef ExprEngine::processPointerEscapedOnBind( // Cases (1) and (2). const MemRegion *MR = LocAndVal.first.getAsRegion(); const MemSpaceRegion *Space = MR ? MR->getMemorySpace(State) : nullptr; - if (!MR || !isa<StackSpaceRegion, StaticGlobalSpaceRegion>(Space)) { + if (!MR || !isa<StackSpaceRegion, StaticGlobalSpaceRegion, HeapSpaceRegion>( + Space)) { Escaped.push_back(LocAndVal.second); continue; } diff --git a/clang/test/Analysis/NewDeleteLeaks.cpp b/clang/test/Analysis/NewDeleteLeaks.cpp index d9c4b77d1e6fc..e133a653873c7 100644 --- a/clang/test/Analysis/NewDeleteLeaks.cpp +++ b/clang/test/Analysis/NewDeleteLeaks.cpp @@ -251,3 +251,26 @@ void validate_system_header() { } } // namespace protobuf_leak + +// GH214226: Do not treat pointers stored in heap fields as escaped. +namespace heap_field_bind { + +struct Owner { + int *member; + Owner() : member(new int(42)) {} // expected-note {{Memory is allocated}} +}; + +void leak_member_when_owner_deleted() { + Owner *owner = new Owner; // expected-note {{Calling default constructor for 'Owner'}} + // expected-note@-1 {{Returning from default constructor for 'Owner'}} + delete owner; +} // expected-warning {{Potential leak of memory pointed to by field 'member'}} +// expected-note@-1 {{Potential leak of memory pointed to by field 'member'}} + +void member_released_before_owner() { + Owner *owner = new Owner; + delete owner->member; + delete owner; +} // no-warning + +} // namespace heap_field_bind diff --git a/clang/test/Analysis/malloc.c b/clang/test/Analysis/malloc.c index 6c3dadfd16021..0e8a84447c952 100644 --- a/clang/test/Analysis/malloc.c +++ b/clang/test/Analysis/malloc.c @@ -1771,7 +1771,14 @@ void testMallocIntoMalloc(void) { StructWithPtr *s = malloc(sizeof(StructWithPtr)); s->memP = malloc(sizeof(int)); free(s); -} // FIXME: should warn here +} // expected-warning {{Potential leak of memory pointed to by field 'memP'}} + +void testMallocIntoMallocThenFreeMember(void) { + StructWithPtr *s = malloc(sizeof(StructWithPtr)); + s->memP = malloc(sizeof(int)); + free(s->memP); + free(s); +} // no-warning int conjure(void); void testExtent(void) { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
