ziqingluo-90 created this revision.
ziqingluo-90 added reviewers: NoQ, t-rasmud, usama54321, steakhal, vsavchenko, 
martong, Szelethus.
Herald added subscribers: manas, ASDenysPetrov, dkrupp, donat.nagy, 
mikhail.ramalho, a.sidorin, rnkovacs, szepet, baloghadamsoftware, xazax.hun.
Herald added a project: All.
ziqingluo-90 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

When ARC is enabled, (objective-c) block objects are automatically retained and 
released thus they do not leak.  
Without ARC, they still can leak from an expiring stack frame like other stack 
variables.

This patch simply adds the ARC checking condition onto the changes made in 
https://reviews.llvm.org/D107078 <https://reviews.llvm.org/D107078>.   The 
checker already checks the same condition for global variables.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D131009

Files:
  clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
  clang/test/Analysis/stack-capture-leak-arc.mm
  clang/test/Analysis/stack-capture-leak-no-arc.mm

Index: clang/test/Analysis/stack-capture-leak-no-arc.mm
===================================================================
--- clang/test/Analysis/stack-capture-leak-no-arc.mm
+++ clang/test/Analysis/stack-capture-leak-no-arc.mm
@@ -4,6 +4,7 @@
 typedef void (^dispatch_block_t)(void);
 void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
 extern dispatch_queue_t queue;
+void f(int);
 
 void test_block_inside_block_async_no_leak() {
   int x = 123;
@@ -35,3 +36,46 @@
   return outer; // expected-warning-re{{Address of stack-allocated block declared on line {{.+}} is captured by a returned block}}
 }
 
+// The block literal defined in this function could leak once being
+// called.
+void output_block(dispatch_block_t * blk) {
+  int x = 0;
+  *blk = ^{ f(x); }; // expected-warning {{Address of stack-allocated block declared on line 43 is still referred to by the stack variable 'blk' upon returning to the caller.  This will be a dangling reference [core.StackAddressEscape]}}
+}
+
+// The block literal captures nothing thus is treated as a constant.
+void output_constant_block(dispatch_block_t * blk) {
+  *blk = ^{ };
+}
+
+// A block can leak if it captures at least one variable and is not
+// under ARC when its' stack frame expires.
+void test_block_leak() {
+  __block dispatch_block_t blk;
+  int x = 0;
+  dispatch_block_t p = ^{
+    blk = ^{ // expected-warning {{Address of stack-allocated block declared on line 57 is still referred to by the stack variable 'blk' upon returning to the caller.  This will be a dangling reference [core.StackAddressEscape]}}
+      f(x);
+    };
+  };
+
+  p();
+  blk();
+  output_block(&blk);
+  blk();
+}
+
+// A block captures nothing is a constant thus never leaks.
+void test_constant_block_no_leak() {
+  __block dispatch_block_t blk;
+  dispatch_block_t p = ^{
+    blk = ^{
+      f(0);
+    };
+  };
+  
+  p();
+  blk();
+  output_constant_block(&blk);
+  blk();
+}
Index: clang/test/Analysis/stack-capture-leak-arc.mm
===================================================================
--- clang/test/Analysis/stack-capture-leak-arc.mm
+++ clang/test/Analysis/stack-capture-leak-arc.mm
@@ -8,6 +8,7 @@
 typedef long dispatch_time_t;
 void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
 void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
+void f(int);
 
 extern dispatch_queue_t queue;
 extern dispatch_once_t *predicate;
@@ -187,3 +188,40 @@
   }
   dispatch_barrier_sync(queue, ^{});
 }
+
+void output_block(dispatch_block_t * blk) {
+  int x = 0;
+  *blk = ^{ f(x); };
+}
+
+// Block objects themselves can never leak under ARC.
+void test_no_block_leak() {
+  __block dispatch_block_t blk;
+  int x = 0;
+  dispatch_block_t p = ^{
+    blk = ^{
+      f(x);
+    };
+  };
+  p();
+  blk();
+  output_block(&blk);
+  blk();
+}
+
+// Block objects do not leak under ARC but stack variables of
+// non-object kind indirectly referred by a block can leak.
+dispatch_block_t test_block_referencing_variable_leak() {
+  int x = 0;
+  __block int * p = &x;
+  __block int * q = &x;
+  
+  dispatch_async(queue, ^{// expected-warning {{Address of stack memory associated with local variable 'x' is captured by an asynchronously-executed block \
+[alpha.core.StackAddressAsyncEscape]}}
+      ++(*p);
+    });
+  return (dispatch_block_t) ^{// expected-warning {{Address of stack memory associated with local variable 'x' is captured by a returned block \
+[core.StackAddressEscape]}}
+    ++(*q);
+  };
+}
Index: clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -310,6 +310,9 @@
     /// referred by an other stack variable from different stack frame.
     bool checkForDanglingStackVariable(const MemRegion *Referrer,
                                        const MemRegion *Referred) {
+      // Under ARC, blocks are retained and released automatically:
+      if (isArcManagedBlock(Referred, Ctx))
+        return false;
       const auto *ReferrerMemSpace =
           Referrer->getMemorySpace()->getAs<StackSpaceRegion>();
       const auto *ReferredMemSpace =
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to