Skip to content

Commit 30b1d14

Browse files
authored
[analyzer] Fix inf recursion in StackAddrEscapeChecker for self referencing blocks (#169208)
Objective-C blocks are like lambdas. They have captures, just like lambdas. However, they can also implicitly capture themselves unlike lambdas. This means that when walking the captures of a block, we may end up in infinite recursion. This is not possible with lambdas, but happened in practice with blocks downstream. In this patch, I just use a set to keep track of the visited MemRegions. Note that theoretically, there is nothing preventing usual lambdas or functors from falling for the same trap, but probably slightly more difficult to do so. You would likely need a pointer to itself, etc. I'll not speculate here. This inf recursion was likely caused by #126620, released in clang-21. rdar://162215172
1 parent 6413e5a commit 30b1d14

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
2323
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
2424
#include "llvm/ADT/STLExtras.h"
25+
#include "llvm/ADT/SmallPtrSet.h"
2526
#include "llvm/Support/raw_ostream.h"
2627
using namespace clang;
2728
using namespace ento;
@@ -247,6 +248,7 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor {
247248
CheckerContext &Ctxt;
248249
const StackFrameContext *PoppedStackFrame;
249250
SmallVectorImpl<const MemRegion *> &EscapingStackRegions;
251+
llvm::SmallPtrSet<const MemRegion *, 16> VisitedRegions;
250252

251253
public:
252254
explicit FindStackRegionsSymbolVisitor(
@@ -258,6 +260,9 @@ class FindStackRegionsSymbolVisitor final : public SymbolVisitor {
258260
bool VisitSymbol(SymbolRef sym) override { return true; }
259261

260262
bool VisitMemRegion(const MemRegion *MR) override {
263+
if (!VisitedRegions.insert(MR).second)
264+
return true;
265+
261266
SaveIfEscapes(MR);
262267

263268
if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>())

clang/test/Analysis/stackaddrleak.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc -verify -std=c99 -Dbool=_Bool -Wno-bool-conversion %s
2-
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc -verify -x c++ -Wno-bool-conversion %s
1+
// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,unix.Malloc -verify -std=c99 -Dbool=_Bool -Wno-bool-conversion %s
2+
// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,unix.Malloc -verify -x c++ -Wno-bool-conversion %s
33

44
typedef __INTPTR_TYPE__ intptr_t;
55
char const *p;
@@ -90,3 +90,14 @@ struct child_stack_context_s return_child_stack_context_field() {
9090
}
9191
return s; // expected-warning {{Address of stack memory associated with local variable 'a' returned to caller}}
9292
}
93+
94+
// Returns an 'int' block taking an 'int'.
95+
int (^copy_self_referencing_block(void))(int) {
96+
// It is important that the 'fib' block captures itself.
97+
__block int (^fib)(int) = ^(int n) {
98+
if (n <= 1) return n;
99+
return fib(n - 1) + fib(n - 2);
100+
};
101+
return fib; // no-crash when copying a self-referencing 'fib'
102+
// expected-warning-re@-1 {{Address of stack-allocated block declared on line {{[0-9]+}} is captured by a returned block}}
103+
}

0 commit comments

Comments
 (0)