https://github.com/steakhal created 
https://github.com/llvm/llvm-project/pull/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

From c724b0b97421bdfae6a632a59c659ad809362b2e Mon Sep 17 00:00:00 2001
From: Balazs Benics <[email protected]>
Date: Sun, 23 Nov 2025 11:05:37 +0100
Subject: [PATCH] [analyzer] Fix inf recursion in StackAddrEscapeChecker for
 self referencing blocks

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.

rdar://162215172
---
 .../Checkers/StackAddrEscapeChecker.cpp           |  5 +++++
 clang/test/Analysis/stackaddrleak.c               | 15 +++++++++++++--
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp 
b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index 019e81f91400d..027bf780273cc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -22,6 +22,7 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/Support/raw_ostream.h"
 using namespace clang;
 using namespace ento;
@@ -247,6 +248,7 @@ class FindStackRegionsSymbolVisitor final : public 
SymbolVisitor {
   CheckerContext &Ctxt;
   const StackFrameContext *PoppedStackFrame;
   SmallVectorImpl<const MemRegion *> &EscapingStackRegions;
+  llvm::SmallPtrSet<const MemRegion *, 16> VisitedRegions;
 
 public:
   explicit FindStackRegionsSymbolVisitor(
@@ -258,6 +260,9 @@ class FindStackRegionsSymbolVisitor final : public 
SymbolVisitor {
   bool VisitSymbol(SymbolRef sym) override { return true; }
 
   bool VisitMemRegion(const MemRegion *MR) override {
+    if (!VisitedRegions.insert(MR).second)
+      return true;
+
     SaveIfEscapes(MR);
 
     if (const BlockDataRegion *BDR = MR->getAs<BlockDataRegion>())
diff --git a/clang/test/Analysis/stackaddrleak.c 
b/clang/test/Analysis/stackaddrleak.c
index 95175996e8274..96bd4e4ea19e5 100644
--- a/clang/test/Analysis/stackaddrleak.c
+++ b/clang/test/Analysis/stackaddrleak.c
@@ -1,5 +1,5 @@
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc -verify -std=c99 
-Dbool=_Bool -Wno-bool-conversion %s
-// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc -verify -x c++ 
-Wno-bool-conversion %s
+// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,unix.Malloc -verify 
-std=c99 -Dbool=_Bool -Wno-bool-conversion %s
+// RUN: %clang_analyze_cc1 -fblocks -analyzer-checker=core,unix.Malloc -verify 
-x c++ -Wno-bool-conversion %s
 
 typedef __INTPTR_TYPE__ intptr_t;
 char const *p;
@@ -90,3 +90,14 @@ struct child_stack_context_s 
return_child_stack_context_field() {
   }
   return s; // expected-warning {{Address of stack memory associated with 
local variable 'a' returned to caller}}
 }
+
+// Returns an 'int' block taking an 'int'.
+int (^copy_self_referencing_block(void))(int) {
+  // It is important that the 'fib' block captures itself.
+  __block int (^fib)(int) = ^(int n) {
+    if (n <= 1) return n;
+    return fib(n - 1) + fib(n - 2);
+  };
+  return fib; // no-crash when copying a self-referencing 'fib'
+  // expected-warning-re@-1 {{Address of stack-allocated block declared on 
line {{[0-9]+}} is captured by a returned block}}
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to