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

Add positive tests


Repository:
  rL LLVM

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,97 @@
+// RUN: %clang_analyze_cc1 -triple x86_64-apple-darwin10 -analyzer-checker=core -analyzer-store=region -fblocks -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' was captured by the block \
+which will be executed asynchronously}}
+}
+
+void test_block_expr_once() {
+  int x = 123;
+  int *p = &x;
+  dispatch_once(predicate, ^{
+    *p = 321;
+  });
+  // expected-warning@-3 {{Address of stack memory associated with local variable 'x' was captured by the block \
+which will be executed asynchronously}}
+}
+
+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' was captured by the block \
+which will be executed asynchronously}}
+}
+
+void test_block_expr_async_no_leak() {
+  int x = 123;
+  int *p = &x;
+
+  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' was captured by the block \
+which will be executed asynchronously}}
+}
+
+void test_block_var_once() {
+  int x = 123;
+  int *p = &x;
+  void (^b)(void) = ^void(void) {
+    *p = 1; 
+  };
+  dispatch_once(predicate, b);
+  // expected-warning@-1 {{Address of stack memory associated with local variable 'x' was captured by the block \
+which will be executed asynchronously}}
+}
+
+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' was captured by the block \
+which will be executed asynchronously}}
+}
+
+void test_block_var_async_no_leak() {
+  int x = 123;
+  int *p = &x;
+  void (^b)(void) = ^void(void) {
+    int y = x;
+    ++y; 
+  };
+  dispatch_async(queue, b);
+}
Index: lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -18,20 +18,24 @@
 #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:
@@ -92,8 +96,9 @@
   return range;
 }
 
-void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R,
-                                          const Expr *RetE) const {
+void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
+                                            const MemRegion *R,
+                                            const Expr *RetE) const {
   ExplodedNode *N = C.generateErrorNode();
 
   if (!N)
@@ -116,6 +121,47 @@
   C.emitReport(std::move(report));
 }
 
+void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
+                                          CheckerContext &C) const {
+  if (!Call.isGlobalCFunction("dispatch_after") &&
+      !Call.isGlobalCFunction("dispatch_async") &&
+      !Call.isGlobalCFunction("dispatch_once"))
+    return;
+
+  for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
+    const BlockDataRegion *Block =
+        dyn_cast_or_null<BlockDataRegion>(Call.getArgSVal(Idx).getAsRegion());
+    if (!Block)
+      continue;
+    BlockDataRegion::referenced_vars_iterator I =
+        Block->referenced_vars_begin();
+    BlockDataRegion::referenced_vars_iterator E = Block->referenced_vars_end();
+    for (; I != E; ++I) {
+      SVal Val = Call.getState()->getSVal(I.getCapturedRegion());
+      const MemRegion *Region = Val.getAsRegion();
+      if (!Region)
+        return;
+      if (dyn_cast_or_null<StackSpaceRegion>(Region->getMemorySpace())) {
+        ExplodedNode *N = C.generateErrorNode();
+        if (!N)
+          return;
+        if (!BT_capturestackleak)
+          BT_capturestackleak = llvm::make_unique<BuiltinBug>(
+              this, "Capture of address of stack-allocated memory");
+        SmallString<512> Buf;
+        llvm::raw_svector_ostream Out(Buf);
+        genName(Out, Region, C.getASTContext());
+        Out << " was captured by the block which will be executed "
+               "asynchronously";
+        auto report =
+            llvm::make_unique<BugReport>(*BT_capturestackleak, Out.str(), N);
+        report->addRange(Call.getArgExpr(Idx)->getSourceRange());
+        C.emitReport(std::move(report));
+      }
+    }
+  }
+}
+
 void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
                                           CheckerContext &C) const {
 
@@ -132,7 +178,7 @@
     return;
 
   const StackSpaceRegion *SS =
-    dyn_cast_or_null<StackSpaceRegion>(R->getMemorySpace());
+      dyn_cast_or_null<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