Hi,

I have made a new checker similar to the divison by zero i previously submitted.

This one checks for dereferences that are later compared against zero.

As requested i have made it CFG-based like how DeadStores is implemented. I 
hope this is how you meant.

//Anders
Index: include/clang/Analysis/Analyses/TestAfter.h
===================================================================
--- include/clang/Analysis/Analyses/TestAfter.h	(revision 0)
+++ include/clang/Analysis/Analyses/TestAfter.h	(working copy)
@@ -0,0 +1,71 @@
+//===- TestAfter.h - Test after usage analysis for Source CFGs ----*- C++ --*-//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements test after usage analysis for source-level CFGs.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYSIS_ANALYSES_TESTAFTER_H
+#define LLVM_CLANG_ANALYSIS_ANALYSES_TESTAFTER_H
+
+#include "clang/AST/Decl.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+
+namespace clang {
+
+class CFG;
+class CFGBlock;
+class Stmt;
+class DeclRefExpr;
+class SourceManager;
+
+class TestAfter : public ManagedAnalysis {
+public:
+  class InvalidatedValues {
+  public:
+    llvm::ImmutableSet<const VarDecl *> Derefs;
+
+    InvalidatedValues() : Derefs(nullptr) {}
+
+    InvalidatedValues(llvm::ImmutableSet<const VarDecl *> Derefs)
+        : Derefs(Derefs) {}
+
+    ~InvalidatedValues() {}
+
+    bool isDereferenced(const VarDecl *D) const;
+  };
+
+  class Observer {
+  public:
+    virtual ~Observer() {}
+
+    /// A callback invoked right before invoking the
+    ///  test after transfer function on the given expression.
+    virtual void observeVarDecl(const DeclRefExpr *S,
+                                const InvalidatedValues &V) {}
+  };
+
+  virtual ~TestAfter();
+
+  void runOnAllBlocks(Observer &Obs);
+
+  static TestAfter *create(AnalysisDeclContext &AnalysisContext);
+
+  static const void *getTag();
+
+private:
+  TestAfter(void *Impl);
+  void *Impl;
+};
+
+} // end namespace clang
+
+#endif
Index: lib/Analysis/CMakeLists.txt
===================================================================
--- lib/Analysis/CMakeLists.txt	(revision 217022)
+++ lib/Analysis/CMakeLists.txt	(working copy)
@@ -23,6 +23,7 @@
   PseudoConstantAnalysis.cpp
   ReachableCode.cpp
   ScanfFormatString.cpp
+  TestAfter.cpp
   ThreadSafety.cpp
   ThreadSafetyCommon.cpp
   ThreadSafetyLogical.cpp
Index: lib/Analysis/TestAfter.cpp
===================================================================
--- lib/Analysis/TestAfter.cpp	(revision 0)
+++ lib/Analysis/TestAfter.cpp	(working copy)
@@ -0,0 +1,165 @@
+//=- TestAfter.cpp - Test after usage analysis for Source CFGs ----*- C++ --*-//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements test after usage analysis for source-level CFGs.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Analyses/TestAfter.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/Analyses/DataflowWorklist.h"
+#include "clang/Analysis/AnalysisContext.h"
+#include "clang/Analysis/CFG.h"
+#include "llvm/ADT/DenseMap.h"
+
+using namespace clang;
+
+namespace {
+class TestAfterImpl {
+public:
+  AnalysisDeclContext &AnalysisContext;
+  llvm::ImmutableSet<const VarDecl *>::Factory DSetFact;
+  llvm::DenseMap<const CFGBlock *, TestAfter::InvalidatedValues>
+      BlocksToDereferenced;
+
+  TestAfter::InvalidatedValues runOnBlock(const CFGBlock *Block,
+                                           TestAfter::InvalidatedValues DerefVal,
+                                           TestAfter::Observer *Obs = nullptr);
+
+  TestAfterImpl(AnalysisDeclContext &AC)
+      : AnalysisContext(AC),
+        DSetFact(false) // This is a *major* performance win.
+  {}
+};
+}
+
+static TestAfterImpl &getImpl(void *X) { return *((TestAfterImpl *)X); }
+
+//===----------------------------------------------------------------------===//
+// Operations and queries on InvalidatedValues.
+//===----------------------------------------------------------------------===//
+
+bool TestAfter::InvalidatedValues::isDereferenced(const VarDecl *D) const {
+  return Derefs.contains(D);
+}
+
+//===----------------------------------------------------------------------===//
+// Dataflow computation.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class TransferFunctions : public StmtVisitor<TransferFunctions> {
+  TestAfterImpl &LV;
+  TestAfter::InvalidatedValues &DerefVal;
+  TestAfter::Observer *Observer;
+  const CFGBlock *CurrentBlock;
+
+public:
+  TransferFunctions(TestAfterImpl &Im, TestAfter::InvalidatedValues &Val,
+                    TestAfter::Observer *Observer, const CFGBlock *CurrentBlock)
+      : LV(Im), DerefVal(Val), Observer(Observer), CurrentBlock(CurrentBlock) {}
+
+  void VisitBinaryOperator(BinaryOperator *BO);
+  void VisitUnaryOperator(UnaryOperator *UO);
+};
+}
+
+void TransferFunctions::VisitBinaryOperator(BinaryOperator *B) {
+  if (B->isComparisonOp()) {
+    const IntegerLiteral *IntLiteral =
+        dyn_cast<IntegerLiteral>(B->getRHS()->IgnoreImpCasts());
+    bool LRHS = true;
+    if (!IntLiteral) {
+      IntLiteral = dyn_cast<IntegerLiteral>(B->getLHS()->IgnoreImpCasts());
+      LRHS = false;
+    }
+
+    if (!IntLiteral || IntLiteral->getValue() != 0)
+      return;
+
+    Expr *Val =
+        LRHS ? B->getLHS()->IgnoreImpCasts() : B->getRHS()->IgnoreImpCasts();
+    if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Val))
+      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
+        DerefVal.Derefs = LV.DSetFact.add(DerefVal.Derefs, VD);
+  }
+}
+
+void TransferFunctions::VisitUnaryOperator(UnaryOperator *UO) {
+
+  switch (UO->getOpcode()) {
+  default:
+    return;
+  case UO_Deref:
+    if (Observer)
+      if (const DeclRefExpr *DR =
+              dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreImpCasts()))
+        Observer->observeVarDecl(DR, DerefVal);
+    break;
+  case UO_LNot:
+    if (const DeclRefExpr *DR =
+            dyn_cast<DeclRefExpr>(UO->getSubExpr()->IgnoreImpCasts()))
+      if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
+        DerefVal.Derefs = LV.DSetFact.add(DerefVal.Derefs, VD);
+    break;
+  }
+}
+
+TestAfter::InvalidatedValues
+TestAfterImpl::runOnBlock(const CFGBlock *Block,
+                          TestAfter::InvalidatedValues Val,
+                          TestAfter::Observer *Obs) {
+
+  TransferFunctions TF(*this, Val, Obs, Block);
+
+  // Visit the terminator (if any).
+  if (const Stmt *Term = Block->getTerminator())
+    TF.Visit(const_cast<Stmt *>(Term));
+
+  // Apply the transfer function for all Stmts in the block.
+  for (CFGBlock::const_reverse_iterator I = Block->rbegin(),
+                                        E = Block->rend();
+       I != E; ++I) {
+    const CFGElement &Elem = *I;
+
+    if (!Elem.getAs<CFGStmt>())
+      continue;
+
+    const Stmt *S = Elem.castAs<CFGStmt>().getStmt();
+    TF.Visit(const_cast<Stmt *>(S));
+  }
+  return Val;
+}
+
+void TestAfter::runOnAllBlocks(TestAfter::Observer &Obs) {
+  const CFG *Cfg = getImpl(Impl).AnalysisContext.getCFG();
+  for (CFG::const_iterator I = Cfg->begin(), E = Cfg->end(); I != E; ++I)
+    getImpl(Impl)
+        .runOnBlock(*I, getImpl(Impl).BlocksToDereferenced[*I], &Obs);
+}
+
+TestAfter::TestAfter(void *Im) : Impl(Im) {}
+
+TestAfter::~TestAfter() { delete (TestAfterImpl *)Impl; }
+
+TestAfter *TestAfter::create(AnalysisDeclContext &AC) {
+  // No CFG?  Bail out.
+  if (!AC.getCFG())
+    return nullptr;
+
+  TestAfterImpl *LV = new TestAfterImpl(AC);
+
+  return new TestAfter(LV);
+}
+
+const void *TestAfter::getTag() {
+  static int X;
+  return &X;
+}
Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Checkers/CMakeLists.txt	(revision 217022)
+++ lib/StaticAnalyzer/Checkers/CMakeLists.txt	(working copy)
@@ -30,6 +30,7 @@
   DeadStoresChecker.cpp
   DebugCheckers.cpp
   DereferenceChecker.cpp
+  DereferenceThenCheck.cpp
   DirectIvarAssignment.cpp
   DivZeroChecker.cpp
   DynamicTypePropagation.cpp
Index: lib/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- lib/StaticAnalyzer/Checkers/Checkers.td	(revision 217022)
+++ lib/StaticAnalyzer/Checkers/Checkers.td	(working copy)
@@ -100,6 +100,10 @@
   HelpText<"Check for cast from non-struct pointer to struct pointer">,
   DescFile<"CastToStructChecker.cpp">;
 
+def DereferenceThenCheck : Checker<"DereferenceThenCheck">,
+  HelpText<"Check for dereference of a variable that is later compared against 0. Either the comparison is useless or there is a dereference of a null pointer.">,
+  DescFile<"DereferenceThenCheck.cpp">;
+
 def IdenticalExprChecker : Checker<"IdenticalExpr">,
   HelpText<"Warn about unintended use of identical expressions in operators">,
   DescFile<"IdenticalExprChecker.cpp">;
Index: lib/StaticAnalyzer/Checkers/DereferenceThenCheck.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/DereferenceThenCheck.cpp	(revision 0)
+++ lib/StaticAnalyzer/Checkers/DereferenceThenCheck.cpp	(working copy)
@@ -0,0 +1,85 @@
+//==- DereferenceThenCheck.cpp -  Dereference then check checker--- C++ -*--==//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This defines DereferenceThenCheck, a builtin check that performs checks for
+// dereferences where the dereference occurs before comparison with zero.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Analysis/Analyses/TestAfter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class DerefThenCheckObs : public TestAfter::Observer {
+  const CFG &Cfg;
+  BugReporter &BR;
+  const CheckerBase *Checker;
+  AnalysisDeclContext *AC;
+
+public:
+  DerefThenCheckObs(const CFG &Cfg, BugReporter &BR, const CheckerBase *Checker,
+                    AnalysisDeclContext *AC)
+      : Cfg(Cfg), BR(BR), Checker(Checker), AC(AC) {}
+
+  virtual ~DerefThenCheckObs() {}
+
+  bool isDereferenced(const TestAfter::InvalidatedValues &IV,
+                      const VarDecl *D) {
+    return IV.isDereferenced(D);
+  }
+
+  void Report(PathDiagnosticLocation L, SourceRange R) {
+    BR.EmitBasicReport(AC->getDecl(), Checker, "DerefBug", "DerefBug",
+                       "Possible null pointer dereference", L, R);
+  }
+
+  void observeVarDecl(const DeclRefExpr *D,
+                      const TestAfter::InvalidatedValues &IV) override {
+
+    if (const VarDecl *VD = dyn_cast<VarDecl>(D->getDecl()))
+      if (isDereferenced(IV, VD)) {
+        PathDiagnosticLocation ELoc =
+            PathDiagnosticLocation::createBegin(D, BR.getSourceManager(), AC);
+        Report(ELoc, VD->getSourceRange());
+      }
+  }
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// DereferenceThenCheck
+//===----------------------------------------------------------------------===//
+
+namespace {
+class DereferenceThenCheck : public Checker<check::ASTCodeBody> {
+public:
+  void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
+                        BugReporter &BR) const {
+
+    if (TestAfter *TA = Mgr.getAnalysis<TestAfter>(D)) {
+      const CFG &Cfg = *Mgr.getCFG(D);
+      AnalysisDeclContext *AC = Mgr.getAnalysisDeclContext(D);
+      DerefThenCheckObs A(Cfg, BR, this, AC);
+      TA->runOnAllBlocks(A);
+    }
+  }
+};
+}
+
+void ento::registerDereferenceThenCheck(CheckerManager &Mgr) {
+  Mgr.registerChecker<DereferenceThenCheck>();
+}
Index: test/Analysis/dereference-then-check.c
===================================================================
--- test/Analysis/dereference-then-check.c	(revision 0)
+++ test/Analysis/dereference-then-check.c	(working copy)
@@ -0,0 +1,86 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.core -verify %s
+// RUN: %clang_cc1 -x c++ -analyze -analyzer-checker=core,alpha.core -verify %s
+void foo1(int *p) {
+  *p = 0; // expected-warning {{Possible null pointer dereference}}
+  if (!p) {
+  }
+}
+
+void bar(int p) {}
+void foo2(int *p) {
+  bar(*p); // expected-warning {{Possible null pointer dereference}}
+  if (!p) {
+  }
+}
+
+void foo3(int *p) {
+  *p = 0; // expected-warning {{Possible null pointer dereference}}
+  if (p == 0) {
+  }
+}
+
+//False negatives
+
+void foo5(char *p) {
+  if (*p == 0) {
+  }
+  if (!p) {
+  }
+}
+
+void foo55(char *p) {
+  if (1)
+    if (*p == 0) {
+    }
+  if (!p) {
+  }
+}
+
+void foo0(int *p, int a) {
+  if (*p == 0) {
+  }
+  if (*p == 0) {
+  }
+  if (p == 0) {
+  }
+}
+
+void f(int **p);
+void foo6() {
+  int *p;
+  f(&p);
+  if (!p) {
+  }
+}
+
+int *w;
+int **ff() { return &w; }
+
+void foo7() {
+  int **p = ff();
+  if (!p) {
+  }
+}
+
+int x;
+void foo8(int *p) {
+  if (x)
+    p = 0;
+  else
+    *p = 0;
+  if (!p) {
+  }
+}
+
+void foo9(int *p) {
+  int var1 = p ? *p : 0;
+  if (!p) {
+  }
+}
+
+void foo10(int *p) {
+  if (p)
+    *p = 0;
+  if (!p) {
+  }
+}
Index: test/Analysis/func.c
===================================================================
--- test/Analysis/func.c	(revision 217022)
+++ test/Analysis/func.c	(working copy)
@@ -23,7 +23,7 @@
   clang_analyzer_eval(!f); // expected-warning{{FALSE}}
 
   clang_analyzer_eval(!g); // expected-warning{{UNKNOWN}}
-  (*g)();
+  (*g)();                  // expected-warning {{Possible null pointer dereference}}
   clang_analyzer_eval(!g); // expected-warning{{FALSE}}
 }
 
Index: test/Analysis/reference.cpp
===================================================================
--- test/Analysis/reference.cpp	(revision 217022)
+++ test/Analysis/reference.cpp	(working copy)
@@ -111,7 +111,7 @@
   // value is non-null in the implementation of references, so it is possible
   // to produce a supposed "null reference" at runtime. The analyzer should
   // still warn when it can prove such errors.
-  int &y = *x;
+  int &y = *x;  // expected-warning {{Possible null pointer dereference}}
   if (x != 0)
     return;
   y = 5; // expected-warning{{Dereference of null pointer}}
@@ -164,7 +164,7 @@
 	clang_analyzer_eval(ptr == 0); // expected-warning{{UNKNOWN}}
 
 	extern void use(int &ref);
-	use(*ptr);
+	use(*ptr);  // expected-warning {{Possible null pointer dereference}}
 
 	clang_analyzer_eval(ptr == 0); // expected-warning{{FALSE}}
 }
Index: test/Analysis/weak-functions.c
===================================================================
--- test/Analysis/weak-functions.c	(revision 217022)
+++ test/Analysis/weak-functions.c	(working copy)
@@ -61,7 +61,7 @@
   clang_analyzer_eval(!f); // expected-warning{{FALSE}}
 
   clang_analyzer_eval(!g); // expected-warning{{UNKNOWN}}
-  (*g)();
+  (*g)();                  // expected-warning {{Possible null pointer dereference}}
   clang_analyzer_eval(!g); // expected-warning{{FALSE}}
 }
 
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to