[cfe-commits] r161214 - in /cfe/trunk: lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp test/Analysis/initializer.cpp test/Analysis/misc-ps-region-store.cpp test/Analysis/reference.cpp

Jordan Rose jordan_rose at apple.com
Thu Aug 2 14:33:42 PDT 2012


Author: jrose
Date: Thu Aug  2 16:33:42 2012
New Revision: 161214

URL: http://llvm.org/viewvc/llvm-project?rev=161214&view=rev
Log:
[analyzer] Add a simple check for initializing reference variables with null.

There's still more work to be done here; this doesn't catch reference
parameters or return values. But it's a step in the right direction.

Part of <rdar://problem/11212286>.

Modified:
    cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
    cfe/trunk/test/Analysis/initializer.cpp
    cfe/trunk/test/Analysis/misc-ps-region-store.cpp
    cfe/trunk/test/Analysis/reference.cpp

Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp?rev=161214&r1=161213&r2=161214&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp Thu Aug  2 16:33:42 2012
@@ -26,13 +26,18 @@
 namespace {
 class DereferenceChecker
     : public Checker< check::Location,
-                        EventDispatcher<ImplicitNullDerefEvent> > {
+                      check::Bind,
+                      EventDispatcher<ImplicitNullDerefEvent> > {
   mutable OwningPtr<BuiltinBug> BT_null;
   mutable OwningPtr<BuiltinBug> BT_undef;
 
+  void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C,
+                 bool IsBind = false) const;
+
 public:
   void checkLocation(SVal location, bool isLoad, const Stmt* S,
                      CheckerContext &C) const;
+  void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
 
   static const MemRegion *AddDerefSource(raw_ostream &os,
                              SmallVectorImpl<SourceRange> &Ranges,
@@ -76,6 +81,108 @@
   return sourceR;
 }
 
+void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
+                                   CheckerContext &C, bool IsBind) const {
+  // Generate an error node.
+  ExplodedNode *N = C.generateSink(State);
+  if (!N)
+    return;
+
+  // We know that 'location' cannot be non-null.  This is what
+  // we call an "explicit" null dereference.
+  if (!BT_null)
+    BT_null.reset(new BuiltinBug("Dereference of null pointer"));
+
+  SmallString<100> buf;
+  SmallVector<SourceRange, 2> Ranges;
+
+  // Walk through lvalue casts to get the original expression
+  // that syntactically caused the load.
+  if (const Expr *expr = dyn_cast<Expr>(S))
+    S = expr->IgnoreParenLValueCasts();
+
+  const MemRegion *sourceR = 0;
+
+  if (IsBind) {
+    if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) {
+      if (BO->isAssignmentOp())
+        S = BO->getRHS();
+    } else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
+      assert(DS->isSingleDecl() && "We process decls one by one");
+      if (const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
+        if (const Expr *Init = VD->getAnyInitializer())
+          S = Init;
+    }
+  }
+
+  switch (S->getStmtClass()) {
+  case Stmt::ArraySubscriptExprClass: {
+    llvm::raw_svector_ostream os(buf);
+    os << "Array access";
+    const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
+    sourceR = AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
+                             State.getPtr(), N->getLocationContext());
+    os << " results in a null pointer dereference";
+    break;
+  }
+  case Stmt::UnaryOperatorClass: {
+    llvm::raw_svector_ostream os(buf);
+    os << "Dereference of null pointer";
+    const UnaryOperator *U = cast<UnaryOperator>(S);
+    sourceR = AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
+                             State.getPtr(), N->getLocationContext(), true);
+    break;
+  }
+  case Stmt::MemberExprClass: {
+    const MemberExpr *M = cast<MemberExpr>(S);
+    if (M->isArrow()) {
+      llvm::raw_svector_ostream os(buf);
+      os << "Access to field '" << M->getMemberNameInfo()
+         << "' results in a dereference of a null pointer";
+      sourceR = AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
+                               State.getPtr(), N->getLocationContext(), true);
+    }
+    break;
+  }
+  case Stmt::ObjCIvarRefExprClass: {
+    const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
+    if (const DeclRefExpr *DR =
+        dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) {
+      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+        llvm::raw_svector_ostream os(buf);
+        os << "Instance variable access (via '" << VD->getName()
+           << "') results in a null pointer dereference";
+      }
+    }
+    Ranges.push_back(IV->getSourceRange());
+    break;
+  }
+  default:
+    break;
+  }
+
+  BugReport *report =
+    new BugReport(*BT_null,
+                  buf.empty() ? BT_null->getDescription() : buf.str(),
+                  N);
+
+  report->addVisitor(
+    bugreporter::getTrackNullOrUndefValueVisitor(N,
+                                                 bugreporter::GetDerefExpr(N),
+                                                 report));
+
+  for (SmallVectorImpl<SourceRange>::iterator
+       I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
+    report->addRange(*I);
+
+  if (sourceR) {
+    report->markInteresting(sourceR);
+    report->markInteresting(State->getRawSVal(loc::MemRegionVal(sourceR)));
+  }
+
+  C.EmitReport(report);
+}
+
 void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
                                        CheckerContext &C) const {
   // Check for dereference of an undefined value.
@@ -101,115 +208,66 @@
     return;
 
   ProgramStateRef state = C.getState();
-  const LocationContext *LCtx = C.getLocationContext();
+
   ProgramStateRef notNullState, nullState;
   llvm::tie(notNullState, nullState) = state->assume(location);
 
   // The explicit NULL case.
   if (nullState) {
     if (!notNullState) {
-      // Generate an error node.
-      ExplodedNode *N = C.generateSink(nullState);
-      if (!N)
-        return;
-
-      // We know that 'location' cannot be non-null.  This is what
-      // we call an "explicit" null dereference.
-      if (!BT_null)
-        BT_null.reset(new BuiltinBug("Dereference of null pointer"));
-
-      SmallString<100> buf;
-      SmallVector<SourceRange, 2> Ranges;
-      
-      // Walk through lvalue casts to get the original expression
-      // that syntactically caused the load.
-      if (const Expr *expr = dyn_cast<Expr>(S))
-        S = expr->IgnoreParenLValueCasts();
-      
-      const MemRegion *sourceR = 0;
-
-      switch (S->getStmtClass()) {
-        case Stmt::ArraySubscriptExprClass: {
-          llvm::raw_svector_ostream os(buf);
-          os << "Array access";
-          const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
-          sourceR =
-            AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
-                           state.getPtr(), LCtx);
-          os << " results in a null pointer dereference";
-          break;
-        }
-        case Stmt::UnaryOperatorClass: {
-          llvm::raw_svector_ostream os(buf);
-          os << "Dereference of null pointer";
-          const UnaryOperator *U = cast<UnaryOperator>(S);
-          sourceR =
-            AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
-                           state.getPtr(), LCtx, true);
-          break;
-        }
-        case Stmt::MemberExprClass: {
-          const MemberExpr *M = cast<MemberExpr>(S);
-          if (M->isArrow()) {
-            llvm::raw_svector_ostream os(buf);
-            os << "Access to field '" << M->getMemberNameInfo()
-               << "' results in a dereference of a null pointer";
-            sourceR =
-              AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
-                             state.getPtr(), LCtx, true);
-          }
-          break;
-        }
-        case Stmt::ObjCIvarRefExprClass: {
-          const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
-          if (const DeclRefExpr *DR =
-              dyn_cast<DeclRefExpr>(IV->getBase()->IgnoreParenCasts())) {
-            if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
-              llvm::raw_svector_ostream os(buf);
-              os << "Instance variable access (via '" << VD->getName()
-                 << "') results in a null pointer dereference";
-            }
-          }
-          Ranges.push_back(IV->getSourceRange());
-          break;
-        }
-        default:
-          break;
-      }
+      reportBug(nullState, S, C);
+      return;
+    }
 
-      BugReport *report =
-        new BugReport(*BT_null,
-                              buf.empty() ? BT_null->getDescription():buf.str(),
-                              N);
+    // Otherwise, we have the case where the location could either be
+    // null or not-null.  Record the error node as an "implicit" null
+    // dereference.
+    if (ExplodedNode *N = C.generateSink(nullState)) {
+      ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
+      dispatchEvent(event);
+    }
+  }
 
-      report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N,
-                                        bugreporter::GetDerefExpr(N), report));
+  // From this point forward, we know that the location is not null.
+  C.addTransition(notNullState);
+}
 
-      for (SmallVectorImpl<SourceRange>::iterator
-            I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
-        report->addRange(*I);
-
-      if (sourceR) {
-        report->markInteresting(sourceR);
-        report->markInteresting(state->getRawSVal(loc::MemRegionVal(sourceR)));
-      }
+void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
+                                   CheckerContext &C) const {
+  // If we're binding to a reference, check if the value is potentially null.
+  if (V.isUndef())
+    return;
 
-      C.EmitReport(report);
+  const MemRegion *MR = L.getAsRegion();
+  const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
+  if (!TVR)
+    return;
+
+  if (!TVR->getValueType()->isReferenceType())
+    return;
+
+  ProgramStateRef State = C.getState();
+
+  ProgramStateRef StNonNull, StNull;
+  llvm::tie(StNonNull, StNull) = State->assume(cast<DefinedOrUnknownSVal>(V));
+
+  if (StNull) {
+    if (!StNonNull) {
+      reportBug(StNull, S, C, /*isBind=*/true);
       return;
     }
-    else {
-      // Otherwise, we have the case where the location could either be
-      // null or not-null.  Record the error node as an "implicit" null
-      // dereference.
-      if (ExplodedNode *N = C.generateSink(nullState)) {
-        ImplicitNullDerefEvent event = { l, isLoad, N, &C.getBugReporter() };
-        dispatchEvent(event);
-      }
+
+    // At this point the value could be either null or non-null.
+    // Record this as an "implicit" null dereference.
+    if (ExplodedNode *N = C.generateSink(StNull)) {
+      ImplicitNullDerefEvent event = { V, /*isLoad=*/true, N,
+                                       &C.getBugReporter() };
+      dispatchEvent(event);
     }
   }
 
-  // From this point forward, we know that the location is not null.
-  C.addTransition(notNullState);
+  // From here on out, assume the value is non-null.
+  C.addTransition(StNonNull);
 }
 
 void ento::registerDereferenceChecker(CheckerManager &mgr) {

Modified: cfe/trunk/test/Analysis/initializer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/initializer.cpp?rev=161214&r1=161213&r2=161214&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/initializer.cpp (original)
+++ cfe/trunk/test/Analysis/initializer.cpp Thu Aug  2 16:33:42 2012
@@ -43,3 +43,24 @@
   IndirectMember obj(3);
   clang_analyzer_eval(obj.getX() == 3); // expected-warning{{TRUE}}
 }
+
+
+// ------------------------------------
+// False negatives
+// ------------------------------------
+
+struct RefWrapper {
+  RefWrapper(int *p) : x(*p) {}
+  RefWrapper(int &r) : x(r) {}
+  int &x;
+};
+
+void testReferenceMember() {
+  int *p = 0;
+  RefWrapper X(p); // should warn in the constructor
+}
+
+void testReferenceMember2() {
+  int *p = 0;
+  RefWrapper X(*p); // should warn here
+}

Modified: cfe/trunk/test/Analysis/misc-ps-region-store.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/misc-ps-region-store.cpp?rev=161214&r1=161213&r2=161214&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/misc-ps-region-store.cpp (original)
+++ cfe/trunk/test/Analysis/misc-ps-region-store.cpp Thu Aug  2 16:33:42 2012
@@ -271,13 +271,27 @@
 const Rdar9212495_A& rdar9212495(const Rdar9212495_C* ptr) {
   const Rdar9212495_A& val = dynamic_cast<const Rdar9212495_A&>(*ptr);
   
+  // This is not valid C++; dynamic_cast with a reference type will throw an
+  // exception if the pointer does not match the expected type.
   if (&val == 0) {
-    val.bar(); // FIXME: This should eventually be a null dereference.
+    val.bar(); // no warning (unreachable)
+    int *p = 0;
+    *p = 0xDEAD; // no warning (unreachable)
   }
   
   return val;
 }
 
+const Rdar9212495_A* rdar9212495_ptr(const Rdar9212495_C* ptr) {
+  const Rdar9212495_A* val = dynamic_cast<const Rdar9212495_A*>(ptr);
+
+  if (val == 0) {
+    val->bar(); // expected-warning{{Called C++ object pointer is null}}
+  }
+
+  return val;
+}
+
 // Test constructors invalidating arguments.  Previously this raised
 // an uninitialized value warning.
 extern "C" void __attribute__((noreturn)) PR9645_exit(int i);

Modified: cfe/trunk/test/Analysis/reference.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/reference.cpp?rev=161214&r1=161213&r2=161214&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/reference.cpp (original)
+++ cfe/trunk/test/Analysis/reference.cpp Thu Aug  2 16:33:42 2012
@@ -90,3 +90,29 @@
     clang_analyzer_eval(s2.x[0] == 42); // expected-warning{{TRUE}}
   }
 }
+
+void testRef() {
+  int *x = 0;
+  int &y = *x; // expected-warning{{Dereference of null pointer}}
+  y = 5;
+}
+
+
+// ------------------------------------
+// False negatives
+// ------------------------------------
+
+namespace rdar11212286 {
+  class B{};
+
+  B test() {
+    B *x = 0;
+    return *x; // should warn here!
+  }
+
+  B &testRef() {
+    B *x = 0;
+    return *x; // should warn here!
+  }
+
+}





More information about the cfe-commits mailing list