1d99bd55aSTed Kremenek //== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==//
2d99bd55aSTed Kremenek //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d99bd55aSTed Kremenek //
7d99bd55aSTed Kremenek //===----------------------------------------------------------------------===//
8d99bd55aSTed Kremenek //
9d99bd55aSTed Kremenek // This defines DivZeroChecker, a builtin check in ExprEngine that performs
10d99bd55aSTed Kremenek // checks for division by zeros.
11d99bd55aSTed Kremenek //
12d99bd55aSTed Kremenek //===----------------------------------------------------------------------===//
13d99bd55aSTed Kremenek 
1476a21502SKristof Umann #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15*82f3ed99STom Ritter #include "clang/StaticAnalyzer/Checkers/Taint.h"
163a02247dSChandler Carruth #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
176a5674ffSArgyrios Kyrtzidis #include "clang/StaticAnalyzer/Core/Checker.h"
18ae468f77SArgyrios Kyrtzidis #include "clang/StaticAnalyzer/Core/CheckerManager.h"
19ae468f77SArgyrios Kyrtzidis #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20d99bd55aSTed Kremenek 
21d99bd55aSTed Kremenek using namespace clang;
22d99bd55aSTed Kremenek using namespace ento;
2344551cf6SArtem Dergachev using namespace taint;
24d99bd55aSTed Kremenek 
25d99bd55aSTed Kremenek namespace {
266a5674ffSArgyrios Kyrtzidis class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > {
27b8984329SAhmed Charles   mutable std::unique_ptr<BuiltinBug> BT;
28e14e591cSHenry Wong   void reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
29e14e591cSHenry Wong                  std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
30e14e591cSHenry Wong 
31d99bd55aSTed Kremenek public:
32ae468f77SArgyrios Kyrtzidis   void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
33d99bd55aSTed Kremenek };
34d99bd55aSTed Kremenek } // end anonymous namespace
35d99bd55aSTed Kremenek 
getDenomExpr(const ExplodedNode * N)36b2cf0063SGeorge Karpenkov static const Expr *getDenomExpr(const ExplodedNode *N) {
37b2cf0063SGeorge Karpenkov   const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
38b2cf0063SGeorge Karpenkov   if (const auto *BE = dyn_cast<BinaryOperator>(S))
39b2cf0063SGeorge Karpenkov     return BE->getRHS();
40b2cf0063SGeorge Karpenkov   return nullptr;
41b2cf0063SGeorge Karpenkov }
42b2cf0063SGeorge Karpenkov 
reportBug(const char * Msg,ProgramStateRef StateZero,CheckerContext & C,std::unique_ptr<BugReporterVisitor> Visitor) const43e14e591cSHenry Wong void DivZeroChecker::reportBug(
44e14e591cSHenry Wong     const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
45e14e591cSHenry Wong     std::unique_ptr<BugReporterVisitor> Visitor) const {
46e39bd407SDevin Coughlin   if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
478298af85SAnna Zaks     if (!BT)
484aca9b1cSAlexander Kornienko       BT.reset(new BuiltinBug(this, "Division by zero"));
498298af85SAnna Zaks 
502f169e7cSArtem Dergachev     auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
51e14e591cSHenry Wong     R->addVisitor(std::move(Visitor));
52b2cf0063SGeorge Karpenkov     bugreporter::trackExpressionValue(N, getDenomExpr(N), *R);
538d3a7a56SAaron Ballman     C.emitReport(std::move(R));
548298af85SAnna Zaks   }
558298af85SAnna Zaks }
568298af85SAnna Zaks 
checkPreStmt(const BinaryOperator * B,CheckerContext & C) const57ae468f77SArgyrios Kyrtzidis void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
58ae468f77SArgyrios Kyrtzidis                                   CheckerContext &C) const {
59d99bd55aSTed Kremenek   BinaryOperator::Opcode Op = B->getOpcode();
60d99bd55aSTed Kremenek   if (Op != BO_Div &&
61d99bd55aSTed Kremenek       Op != BO_Rem &&
62d99bd55aSTed Kremenek       Op != BO_DivAssign &&
63d99bd55aSTed Kremenek       Op != BO_RemAssign)
64d99bd55aSTed Kremenek     return;
65d99bd55aSTed Kremenek 
668698dd63SAnna Zaks   if (!B->getRHS()->getType()->isScalarType())
67d99bd55aSTed Kremenek     return;
68d99bd55aSTed Kremenek 
69d703ec94SGeorge Karpenkov   SVal Denom = C.getSVal(B->getRHS());
7005785d16SDavid Blaikie   Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>();
71d99bd55aSTed Kremenek 
72d99bd55aSTed Kremenek   // Divide-by-undefined handled in the generic checking for uses of
73d99bd55aSTed Kremenek   // undefined values.
74d99bd55aSTed Kremenek   if (!DV)
75d99bd55aSTed Kremenek     return;
76d99bd55aSTed Kremenek 
77d99bd55aSTed Kremenek   // Check for divide by zero.
78d99bd55aSTed Kremenek   ConstraintManager &CM = C.getConstraintManager();
7949b1e38eSTed Kremenek   ProgramStateRef stateNotZero, stateZero;
80867ea1d4SBenjamin Kramer   std::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV);
81d99bd55aSTed Kremenek 
820d58033bSAnna Zaks   if (!stateNotZero) {
830d58033bSAnna Zaks     assert(stateZero);
848298af85SAnna Zaks     reportBug("Division by zero", stateZero, C);
858298af85SAnna Zaks     return;
86d99bd55aSTed Kremenek   }
878298af85SAnna Zaks 
8844551cf6SArtem Dergachev   bool TaintedD = isTainted(C.getState(), *DV);
898298af85SAnna Zaks   if ((stateNotZero && stateZero && TaintedD)) {
90e14e591cSHenry Wong     reportBug("Division by a tainted value, possibly zero", stateZero, C,
912b3d49b6SJonas Devlieghere               std::make_unique<taint::TaintBugVisitor>(*DV));
92d99bd55aSTed Kremenek     return;
93d99bd55aSTed Kremenek   }
94d99bd55aSTed Kremenek 
95d99bd55aSTed Kremenek   // If we get here, then the denom should not be zero. We abandon the implicit
96d99bd55aSTed Kremenek   // zero denom case for now.
97da4c8d68SAnna Zaks   C.addTransition(stateNotZero);
98d99bd55aSTed Kremenek }
99ae468f77SArgyrios Kyrtzidis 
registerDivZeroChecker(CheckerManager & mgr)100ae468f77SArgyrios Kyrtzidis void ento::registerDivZeroChecker(CheckerManager &mgr) {
101ae468f77SArgyrios Kyrtzidis   mgr.registerChecker<DivZeroChecker>();
102ae468f77SArgyrios Kyrtzidis }
103058a7a45SKristof Umann 
shouldRegisterDivZeroChecker(const CheckerManager & mgr)104bda3dd0dSKirstóf Umann bool ento::shouldRegisterDivZeroChecker(const CheckerManager &mgr) {
105058a7a45SKristof Umann   return true;
106058a7a45SKristof Umann }
107