alexshap updated this revision to Diff 121055.
alexshap added a comment.

Address the comments, add more tests


https://reviews.llvm.org/D39438

Files:
  lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
  test/Analysis/stack-async-leak.m

Index: test/Analysis/stack-async-leak.m
===================================================================
--- test/Analysis/stack-async-leak.m
+++ test/Analysis/stack-async-leak.m
@@ -0,0 +1,113 @@
+// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core -fblocks -fobjc-arc -verify %s
+
+typedef struct dispatch_queue_s *dispatch_queue_t;
+typedef void (^dispatch_block_t)(void);
+void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
+typedef long dispatch_once_t;
+void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
+typedef long dispatch_time_t;
+void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
+
+extern dispatch_queue_t queue;
+extern dispatch_once_t *predicate;
+extern dispatch_time_t when;
+
+void test_block_expr_async() {
+  int x = 123;
+  int *p = &x;
+
+  dispatch_async(queue, ^{
+    *p = 321;
+  });
+  // expected-warning@-3 {{Address of stack memory associated with local variable 'x' \
+might leak via the block capture}}
+}
+
+void test_block_expr_once_no_leak() {
+  int x = 123;
+  int *p = &x;
+  // synchronous, no warning
+  dispatch_once(predicate, ^{
+    *p = 321;
+  });
+}
+
+void test_block_expr_after() {
+  int x = 123;
+  int *p = &x;
+  dispatch_after(when, queue, ^{
+    *p = 321;
+  });
+  // expected-warning@-3 {{Address of stack memory associated with local variable 'x' \
+might leak via the block capture}}
+}
+
+void test_block_expr_async_no_leak() {
+  int x = 123;
+  int *p = &x;
+  // no leak
+  dispatch_async(queue, ^{
+    int y = x;
+    ++y;
+  });
+}
+
+void test_block_var_async() {
+  int x = 123;
+  int *p = &x;
+  void (^b)(void) = ^void(void) {
+    *p = 1; 
+  };
+  dispatch_async(queue, b);
+  // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \
+might leak via the block capture}}
+}
+
+dispatch_block_t get_leaking_block() {
+  int leaked_x = 791;
+  int *p = &leaked_x;
+  return ^void(void) {
+    *p = 1; 
+  };
+  // expected-warning@-3 {{Address of stack memory associated with local variable 'leaked_x' \
+might leak via the block capture}}
+}
+
+void test_returned_from_func_block_async() {
+  dispatch_async(queue, get_leaking_block());
+  // expected-warning@-1 {{Address of stack memory associated with local variable 'leaked_x' \
+might leak via the block capture}}
+}
+
+// synchronous, no leak
+void test_block_var_once() {
+  int x = 123;
+  int *p = &x;
+  void (^b)(void) = ^void(void) {
+    *p = 1; 
+  };
+  // no leak
+  dispatch_once(predicate, b);
+}
+
+void test_block_var_after() {
+  int x = 123;
+  int *p = &x;
+  void (^b)(void) = ^void(void) {
+    *p = 1; 
+  };
+  dispatch_after(when, queue, b);
+  // expected-warning@-1 {{Address of stack memory associated with local variable 'x' \
+might leak via the block capture}}
+}
+
+void test_block_var_async_no_leak() {
+  int x = 123;
+  int *p = &x;
+  void (^b)(void) = ^void(void) {
+    int y = x;
+    ++y; 
+  };
+  // no leak
+  dispatch_async(queue, b);
+}
Index: lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -18,23 +18,28 @@
 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
 #include "clang/StaticAnalyzer/Core/Checker.h"
 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/raw_ostream.h"
 using namespace clang;
 using namespace ento;
 
 namespace {
-class StackAddrEscapeChecker : public Checker< check::PreStmt<ReturnStmt>,
+class StackAddrEscapeChecker : public Checker< check::PreCall,
+                                               check::PreStmt<ReturnStmt>,
                                                check::EndFunction > {
   mutable std::unique_ptr<BuiltinBug> BT_stackleak;
   mutable std::unique_ptr<BuiltinBug> BT_returnstack;
+  mutable std::unique_ptr<BuiltinBug> BT_capturestackleak;
 
 public:
+  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
   void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
   void checkEndFunction(CheckerContext &Ctx) const;
 private:
+  void checkBlockCaptures(const BlockDataRegion &B, CheckerContext &C) const;
   void EmitStackError(CheckerContext &C, const MemRegion *R,
                       const Expr *RetE) const;
   static SourceRange genName(raw_ostream &os, const MemRegion *R,
@@ -84,17 +89,17 @@
     Ty.print(os, Ctx.getPrintingPolicy());
     os << "'";
     range = TOR->getExpr()->getSourceRange();
-  }
-  else {
+  } else {
     llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
   }
 
   return range;
 }
 
-void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R,
-                                          const Expr *RetE) const {
-  ExplodedNode *N = C.generateErrorNode();
+void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
+                                            const MemRegion *R,
+                                            const Expr *RetE) const {
+  ExplodedNode *N = C.generateNonFatalErrorNode();
 
   if (!N)
     return;
@@ -116,6 +121,48 @@
   C.emitReport(std::move(report));
 }
 
+void StackAddrEscapeChecker::checkBlockCaptures(const BlockDataRegion &B,
+                                                CheckerContext &C) const {
+  BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin();
+  BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end();
+  for (; I != E; ++I) {
+    SVal Val = C.getState()->getSVal(I.getCapturedRegion());
+    const MemRegion *Region = Val.getAsRegion();
+    if (!Region)
+      continue;
+    if (dyn_cast<StackSpaceRegion>(Region->getMemorySpace())) {
+      ExplodedNode *N = C.generateNonFatalErrorNode();
+      if (!N)
+        continue;
+      if (!BT_capturestackleak)
+        BT_capturestackleak = llvm::make_unique<BuiltinBug>(
+            this, "Address of stack-allocated memory");
+      SmallString<512> Buf;
+      llvm::raw_svector_ostream Out(Buf);
+      SourceRange Range = genName(Out, Region, C.getASTContext());
+      Out << " might leak via the block capture";
+      auto Report =
+          llvm::make_unique<BugReport>(*BT_capturestackleak, Out.str(), N);
+      if (Range.isValid())
+        Report->addRange(Range);
+      C.emitReport(std::move(Report));
+    }
+  }
+}
+
+void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
+                                          CheckerContext &C) const {
+  if (!Call.isGlobalCFunction("dispatch_after") &&
+      !Call.isGlobalCFunction("dispatch_async"))
+    return;
+
+  for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
+    if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
+            Call.getArgSVal(Idx).getAsRegion()))
+      checkBlockCaptures(*B, C);
+  }
+}
+
 void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
                                           CheckerContext &C) const {
 
@@ -131,8 +178,11 @@
   if (!R)
     return;
 
+  if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
+    checkBlockCaptures(*B, C); 
+
   const StackSpaceRegion *SS =
-    dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace());
+      dyn_cast<StackSpaceRegion>(R->getMemorySpace());
 
   if (!SS)
     return;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to