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