Hi,
I have a new patch i'd like to get reviewed for a different kind of division by
zero than the already existing one.
This patch check for division by variable that is later compared against 0.
Either the comparison is useless or there is division by zero.
It catches scenarios like these:
void f(int y) {
x = 100 / y;
if (y == 0) {}
}
//AndersIndex: lib/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- lib/StaticAnalyzer/Checkers/Checkers.td (revision 204458)
+++ lib/StaticAnalyzer/Checkers/Checkers.td (working copy)
@@ -72,6 +72,10 @@
HelpText<"Check for division by zero">,
DescFile<"DivZeroChecker.cpp">;
+def DivZeroChecker2 : Checker<"DivideZero2">,
+ HelpText<"Check for division by variable that is later compared against 0. Either the comparison is useless or there is division by zero.">,
+ DescFile<"DivZeroChecker2.cpp">;
+
def UndefResultChecker : Checker<"UndefinedBinaryOperatorResult">,
HelpText<"Check for undefined results of binary operators">,
DescFile<"UndefResultChecker.cpp">;
Index: lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- lib/StaticAnalyzer/Checkers/CMakeLists.txt (revision 204458)
+++ lib/StaticAnalyzer/Checkers/CMakeLists.txt (working copy)
@@ -32,6 +32,7 @@
DereferenceChecker.cpp
DirectIvarAssignment.cpp
DivZeroChecker.cpp
+ DivZeroChecker2.cpp
DynamicTypePropagation.cpp
ExprInspectionChecker.cpp
FixedAddressChecker.cpp
Index: lib/StaticAnalyzer/Checkers/DivZeroChecker2.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/DivZeroChecker2.cpp (revision 0)
+++ lib/StaticAnalyzer/Checkers/DivZeroChecker2.cpp (revision 0)
@@ -0,0 +1,178 @@
+//== DivZeroChecker2.cpp - Division by zero 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 DivZeroChecker2, a builtin check that performs checks for
+// division by zero where the division occurs before comparison with zero.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangSACheckers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class DivZeroChecker2 : public Checker< check::PreStmt<BinaryOperator>,
+ check::PreStmt<UnaryOperator>,
+ check::PreCall > {
+ mutable std::unique_ptr<BuiltinBug> BB;
+ void reportBug(const char *Msg,
+ ProgramStateRef StateZero,
+ CheckerContext &C) const ;
+public:
+ void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
+ void checkPreStmt(const UnaryOperator *B, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void setDivZeroMap(const SVal &Val, CheckerContext &C) const;
+ void removeDivZeroMap(const SVal &Val, CheckerContext &C) const;
+ const MemRegion* getDivZeroMap(const SVal &Val, const CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(DivZeroMap, const MemRegion *, bool)
+
+void DivZeroChecker2::setDivZeroMap(const SVal &Val, CheckerContext &C) const {
+ const MemRegion *MR = Val.getAsRegion();
+ if (!MR)
+ return;
+
+ ProgramStateRef State = C.getState();
+ const bool Divided = State->get<DivZeroMap>(MR);
+ if (!Divided) {
+ State = State->set<DivZeroMap>(MR, true);
+ C.addTransition(State);
+ }
+}
+
+void DivZeroChecker2::removeDivZeroMap(const SVal &Val,
+ CheckerContext &C) const {
+ const MemRegion *MR = getDivZeroMap(Val, C);
+ if (!MR)
+ return;
+
+ ProgramStateRef State = C.getState();
+ State = State->remove<DivZeroMap>(MR);
+ C.addTransition(State);
+}
+
+const MemRegion* DivZeroChecker2::getDivZeroMap(const SVal &Val,
+ const CheckerContext &C) const {
+ const MemRegion *MR = Val.getAsRegion();
+ if (!MR)
+ return 0;
+
+ ProgramStateRef State = C.getState();
+ const bool Divided = State->get<DivZeroMap>(MR);
+ if (!Divided)
+ return 0;
+ else
+ return MR;
+}
+
+void DivZeroChecker2::reportBug(const char *Msg,
+ ProgramStateRef StateZero,
+ CheckerContext &C) const {
+ if (ExplodedNode *N = C.generateSink(StateZero)) {
+ if (!BB)
+ BB.reset(new BuiltinBug(this, "Division by zero"));
+
+ BugReport *R = new BugReport(*BB, Msg, N);
+ bugreporter::trackNullOrUndefValue(N, bugreporter::GetDenomExpr(N), *R);
+ C.emitReport(R);
+ }
+}
+
+void DivZeroChecker2::checkPreStmt(const BinaryOperator *B,
+ CheckerContext &C) const {
+ BinaryOperator::Opcode Op = B->getOpcode();
+ if (Op == BO_Div ||
+ Op == BO_Rem ||
+ Op == BO_DivAssign ||
+ Op == BO_RemAssign)
+ {
+ if (!B->getRHS()->getType()->isScalarType())
+ return;
+
+ ProgramStateRef State = C.getState();
+ SVal Val = C.getSVal(B->getRHS());
+ Optional<DefinedSVal> DSV = Val.getAs<DefinedSVal>();
+
+ if (!DSV)
+ return;
+
+ // Make sure SVal can be zero.
+ if (!C.getConstraintManager().assume(State, *DSV, false))
+ return;
+
+ Val = C.getSVal(B->getRHS()->IgnoreParenImpCasts());
+ setDivZeroMap(Val, C);
+ } else if (B->isAssignmentOp()) {
+ ProgramStateRef State = C.getState();
+ SVal Val = C.getState()->getSVal(B->getLHS()->IgnoreParenImpCasts(),
+ C.getLocationContext());
+
+ removeDivZeroMap(Val, C);
+ } else if (B->isComparisonOp()) {
+ ProgramStateRef State = C.getState();
+
+ SVal Reg = C.getState()->getSVal(B->getLHS()->IgnoreParenImpCasts(), C.getLocationContext());
+
+ if (!getDivZeroMap(Reg, C))
+ return;
+
+ const IntegerLiteral *IntLiteral = dyn_cast<IntegerLiteral>(B->getRHS());
+ if (IntLiteral && IntLiteral->getValue() == 0)
+ reportBug("Comparison is useless, or there is division by zero previously", State, C);
+ }
+}
+
+void DivZeroChecker2::checkPreStmt(const UnaryOperator *U,
+ CheckerContext &C) const {
+ UnaryOperator::Opcode Op = U->getOpcode();
+ if (U->isIncrementDecrementOp()) {
+ ProgramStateRef State = C.getState();
+ SVal Val = State->getSVal(U->getSubExpr()->IgnoreParenImpCasts(), C.getLocationContext());
+ removeDivZeroMap(Val, C);
+ } else if (Op == UO_LNot) {
+ ProgramStateRef State = C.getState();
+
+ SVal Reg = C.getState()->getSVal(U->getSubExpr()->IgnoreParenImpCasts(), C.getLocationContext());
+
+ if (!getDivZeroMap(Reg, C))
+ return;
+
+ reportBug("Comparison is useless, or there is division by zero previously", State, C);
+ }
+}
+
+void DivZeroChecker2::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const{
+ ProgramStateRef State = C.getState();
+ const Decl *D = Call.getDecl();
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
+ for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) {
+ if (FD && I < FD->getNumParams()) {
+ const ParmVarDecl *ParamDecl = FD->getParamDecl(I);
+ if (ParamDecl->getType()->isPointerType() ||
+ ParamDecl->getType()->isReferenceType()) {
+ SVal Val = Call.getArgSVal(I);
+ removeDivZeroMap(Val, C);
+ }
+ }
+ }
+}
+
+void ento::registerDivZeroChecker2(CheckerManager &mgr) {
+ mgr.registerChecker<DivZeroChecker2>();
+}
+
Index: test/Analysis/div-zero2.cpp
===================================================================
--- test/Analysis/div-zero2.cpp (revision 0)
+++ test/Analysis/div-zero2.cpp (revision 0)
@@ -0,0 +1,109 @@
+// RUN: %clang_cc1 -analyze -analyzer-checker=core.DivideZero2 -verify %s
+
+int var;
+
+void err_eq(int x) {
+ var = 77 / x;
+ if (x==0){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
+
+void err_ne(int x) {
+ var = 77 / x;
+ if (x!=0){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
+
+void err_ge(int x) {
+ var = 77 / x;
+ if (x>=0){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
+
+void err_le(int x) {
+ var = 77 / x;
+ if (x<=0){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
+
+void err_not(int x) {
+ var = 77 / x;
+ if (!x){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
+
+void err_pnot(int x) {
+ int *y = &x;
+ var = 77 / *y;
+ if (!x){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
+
+void err_pnot2(int x) {
+ int *y = &x;
+ var = 77 / x;
+ if (!*y){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
+
+void err_ppnot(int x) {
+ int *y = &x;
+ int **z = &y;
+ var = 77 / **z;
+ if (!x){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
+
+void ok_other(int x, int y) {
+ var = 77 / y;
+ if (x == 0){}
+}
+
+void ok_assign(int x) {
+ var = 77 / x;
+ x = var / 77; // <- assignment => don't warn
+ if (x == 0){}
+}
+
+void ok_dec(int x) {
+ var = 77 / x;
+ x--; // <- assignment => don't warn
+ if (x == 0){}
+}
+
+void ok_inc(int x) {
+ var = 77 / x;
+ x++; // <- assignment => don't warn
+ if (x == 0){}
+}
+
+void do_something_ptr(int*x);
+void ok_callfunc_ptr(int x) {
+ var = 77 / x;
+ do_something_ptr(&x); // <- pass address of x to function => don't warn
+ if (x == 0){}
+}
+
+void do_something_ref(int&x);
+void ok_callfunc_ref(int x) {
+ var = 77 / x;
+ do_something_ref(x); // <- pass reference of x to function => don't warn
+ if (x == 0){}
+}
+
+void do_something(int x);
+void nok_callfunc(int x) {
+ var = 77 / x;
+ do_something(x);
+ if (x == 0){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
+void ok_if(int x) {
+ if (x > 3)
+ var = 77 / x;
+ if (x == 0){}
+}
+
+void nok_if(int x) {
+ if (x < 3)
+ var = 77 / x;
+ if (x == 0){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
+
+void nok_pif(int x) {
+ int *y = &x;
+ if (x < 3)
+ var = 77 / *y;
+ if (x == 0){} // expected-warning {{Comparison is useless, or there is division by zero previously}}
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits