NoQ updated this revision to Diff 35067.
NoQ added a comment.

Thanks for the quick reply, sorry for the delay! Was afk for a couple of days.

Yeah, right, in fact i didn't even fix the issue for store keys at all; only 
for store values and environment values.

It also seems much harder to test store keys, because it's quite a problem to 
guess the symbolic key once the symbol is not present anywhere else, though i 
can imagine an artificial checker that would rely on that. A test like...

  int a[1];
  {
    int x = conjure_index();
    a[x] = 0;
    if (x != 0)
      return;
    clang_analyzer_eval(a[0] == 0); // expected-warning{{TRUE}}
  }
  clang_analyzer_eval(a[0] == 0); // expected-warning{{TRUE}}

...should have exposed such problem, but this kind of lookup doesn't seem to be 
supported by the store yet (that is, the first `expected-warning{{TRUE}}` fails 
as well).

Hmm, what if i expand the `debug.ExprInspection` checker to allow testing 
`SymbolReaper` directly? Updated the diff with a proof of concept, which fixes 
the issue for the store keys and adds a test. I can split the `ExprInspection` 
change into a separate commit/review if necessary. It might be useful for 
testing other `SymbolReaper`-related patches as well.


http://reviews.llvm.org/D12726

Files:
  lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
  lib/StaticAnalyzer/Core/Environment.cpp
  lib/StaticAnalyzer/Core/RegionStore.cpp
  test/Analysis/return-ptr-range.cpp
  test/Analysis/symbol-reaper.c

Index: test/Analysis/symbol-reaper.c
===================================================================
--- test/Analysis/symbol-reaper.c
+++ test/Analysis/symbol-reaper.c
@@ -0,0 +1,23 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=debug.ExprInspection -verify %s
+
+void clang_analyzer_warnOnDeadSymbol(int);
+
+int arr[3];
+
+int conjure_index();
+
+void test_that_expr_inspection_works() {
+  do {
+    int x = conjure_index();
+    clang_analyzer_warnOnDeadSymbol(x);
+  } while(0); // expected-warning{{SYMBOL DEAD}}
+}
+
+void test_element_index_lifetime() {
+  do {
+    int x = conjure_index();
+    clang_analyzer_warnOnDeadSymbol(x);
+    arr[x] = 1;
+    if (x) {}
+  } while(0); // no-warning
+}
Index: test/Analysis/return-ptr-range.cpp
===================================================================
--- test/Analysis/return-ptr-range.cpp
+++ test/Analysis/return-ptr-range.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=alpha.security.ReturnPtrRange -verify %s
+
+int arr[10];
+int *ptr;
+
+int conjure_index();
+
+int *test_element_index_lifetime() {
+  do {
+    int x = conjure_index();
+    ptr = arr + x;
+    if (x != 20)
+      return arr; // no-warning
+  } while (0);
+  return ptr; // expected-warning{{Returned pointer value points outside the original object (potential buffer overflow)}}
+}
+
+int *test_element_index_lifetime_with_local_ptr() {
+  int *local_ptr;
+  do {
+    int x = conjure_index();
+    local_ptr = arr + x;
+    if (x != 20)
+      return arr; // no-warning
+  } while (0);
+  return local_ptr; // expected-warning{{Returned pointer value points outside the original object (potential buffer overflow)}}
+}
Index: lib/StaticAnalyzer/Core/RegionStore.cpp
===================================================================
--- lib/StaticAnalyzer/Core/RegionStore.cpp
+++ lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -2237,8 +2237,20 @@
   if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(baseR))
     SymReaper.markLive(SymR->getSymbol());
 
-  for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I)
+  for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) {
+
+    // Mark the index symbol of any ElementRegion key as live.
+    const BindingKey &K = I.getKey();
+    auto subR = dyn_cast<SubRegion>(K.getRegion());
+    while (subR) {
+      if (auto elemR = dyn_cast<ElementRegion>(subR))
+        if (SymbolRef Sym = elemR->getIndex().getAsSymbol())
+          SymReaper.markLive(Sym);
+      subR = dyn_cast<SubRegion>(subR->getSuperRegion());
+    }
+
     VisitBinding(I.getData());
+  }
 }
 
 void removeDeadBindingsWorker::VisitBinding(SVal V) {
@@ -2260,6 +2272,16 @@
   if (const MemRegion *R = V.getAsRegion()) {
     AddToWorkList(R);
 
+    // Element index of an element region is live.
+    if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
+      SVal Idx = ER->getIndex();
+      for (SymExpr::symbol_iterator SI = Idx.symbol_begin(),
+                                    SE = Idx.symbol_end();
+           SI != SE; ++SI) {
+        SymReaper.markLive(*SI);
+      }
+    }
+
     // All regions captured by a block are also live.
     if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
       BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(),
Index: lib/StaticAnalyzer/Core/Environment.cpp
===================================================================
--- lib/StaticAnalyzer/Core/Environment.cpp
+++ lib/StaticAnalyzer/Core/Environment.cpp
@@ -171,8 +171,15 @@
       EBMapRef = EBMapRef.add(BlkExpr, X);
 
       // If the block expr's value is a memory region, then mark that region.
-      if (Optional<loc::MemRegionVal> R = X.getAs<loc::MemRegionVal>())
-        SymReaper.markLive(R->getRegion());
+      if (Optional<loc::MemRegionVal> R = X.getAs<loc::MemRegionVal>()) {
+        const MemRegion *MR = R->getRegion();
+        SymReaper.markLive(MR);
+
+        // Mark the element index as live.
+        if (const ElementRegion *ER = dyn_cast<ElementRegion>(MR))
+          if (SymbolRef Sym = ER->getIndex().getAsSymbol())
+            SymReaper.markLive(Sym);
+      }
 
       // Mark all symbols in the block expr's value live.
       RSScaner.scan(X);
Index: lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -17,22 +17,26 @@
 using namespace ento;
 
 namespace {
-class ExprInspectionChecker : public Checker< eval::Call > {
+class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols> {
   mutable std::unique_ptr<BugType> BT;
 
   void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
   void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
   void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
   void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
+  void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
 
   typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
                                                  CheckerContext &C) const;
 
 public:
   bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
 };
 }
 
+REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, const void *)
+
 bool ExprInspectionChecker::evalCall(const CallExpr *CE,
                                      CheckerContext &C) const {
   // These checks should have no effect on the surrounding environment
@@ -42,7 +46,10 @@
     .Case("clang_analyzer_checkInlined",
           &ExprInspectionChecker::analyzerCheckInlined)
     .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
-    .Case("clang_analyzer_warnIfReached", &ExprInspectionChecker::analyzerWarnIfReached)
+    .Case("clang_analyzer_warnIfReached",
+          &ExprInspectionChecker::analyzerWarnIfReached)
+    .Case("clang_analyzer_warnOnDeadSymbol",
+          &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
     .Default(nullptr);
 
   if (!Handler)
@@ -137,6 +144,40 @@
       llvm::make_unique<BugReport>(*BT, getArgumentValueString(CE, C), N));
 }
 
+void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
+                                                     CheckerContext &C) const {
+  if (CE->getNumArgs() == 0)
+    return;
+  SVal Val = C.getSVal(CE->getArg(0));
+  SymbolRef Sym = Val.getAsSymbol();
+  if (!Sym)
+    return;
+
+  ProgramStateRef State = C.getState();
+  State = State->add<MarkedSymbols>(Sym);
+  C.addTransition(State);
+}
+
+void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+                                             CheckerContext &C) const {
+  ProgramStateRef State = C.getState();
+  const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
+  for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
+    SymbolRef Sym = static_cast<SymbolRef>(*I);
+    if (!SymReaper.isDead(Sym))
+      continue;
+
+    if (!BT)
+      BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
+
+    ExplodedNode *N = C.generateNonFatalErrorNode();
+    if (!N)
+      return;
+
+    C.emitReport(llvm::make_unique<BugReport>(*BT, "SYMBOL DEAD", N));
+  }
+}
+
 void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
                                           CheckerContext &C) const {
   LLVM_BUILTIN_TRAP;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to