1 //== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This defines DivZeroChecker, a builtin check in ExprEngine that performs 10 // checks for division by zeros. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15 #include "clang/StaticAnalyzer/Checkers/Taint.h" 16 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17 #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" 18 #include "clang/StaticAnalyzer/Core/Checker.h" 19 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 21 #include <optional> 22 23 using namespace clang; 24 using namespace ento; 25 using namespace taint; 26 27 namespace { 28 class DivZeroChecker : public CheckerFamily<check::PreStmt<BinaryOperator>> { 29 void reportBug(StringRef Msg, ProgramStateRef StateZero, 30 CheckerContext &C) const; 31 void reportTaintBug(StringRef Msg, ProgramStateRef StateZero, 32 CheckerContext &C, 33 llvm::ArrayRef<SymbolRef> TaintedSyms) const; 34 35 public: 36 /// This checker family implements two user-facing checker parts. 37 CheckerFrontendWithBugType DivideZeroChecker{"Division by zero"}; 38 CheckerFrontendWithBugType TaintedDivChecker{"Division by zero", 39 categories::TaintedData}; 40 41 void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; 42 43 /// Identifies this checker family for debugging purposes. 44 StringRef getDebugTag() const override { return "DivZeroChecker"; } 45 }; 46 } // end anonymous namespace 47 48 static const Expr *getDenomExpr(const ExplodedNode *N) { 49 const Stmt *S = N->getLocationAs<PreStmt>()->getStmt(); 50 if (const auto *BE = dyn_cast<BinaryOperator>(S)) 51 return BE->getRHS(); 52 return nullptr; 53 } 54 55 void DivZeroChecker::reportBug(StringRef Msg, ProgramStateRef StateZero, 56 CheckerContext &C) const { 57 if (!DivideZeroChecker.isEnabled()) 58 return; 59 if (ExplodedNode *N = C.generateErrorNode(StateZero)) { 60 auto R = 61 std::make_unique<PathSensitiveBugReport>(DivideZeroChecker, Msg, N); 62 bugreporter::trackExpressionValue(N, getDenomExpr(N), *R); 63 C.emitReport(std::move(R)); 64 } 65 } 66 67 void DivZeroChecker::reportTaintBug( 68 StringRef Msg, ProgramStateRef StateZero, CheckerContext &C, 69 llvm::ArrayRef<SymbolRef> TaintedSyms) const { 70 if (!TaintedDivChecker.isEnabled()) 71 return; 72 if (ExplodedNode *N = C.generateErrorNode(StateZero)) { 73 auto R = 74 std::make_unique<PathSensitiveBugReport>(TaintedDivChecker, Msg, N); 75 bugreporter::trackExpressionValue(N, getDenomExpr(N), *R); 76 for (auto Sym : TaintedSyms) 77 R->markInteresting(Sym); 78 C.emitReport(std::move(R)); 79 } 80 } 81 82 void DivZeroChecker::checkPreStmt(const BinaryOperator *B, 83 CheckerContext &C) const { 84 BinaryOperator::Opcode Op = B->getOpcode(); 85 if (Op != BO_Div && 86 Op != BO_Rem && 87 Op != BO_DivAssign && 88 Op != BO_RemAssign) 89 return; 90 91 if (!B->getRHS()->getType()->isScalarType()) 92 return; 93 94 SVal Denom = C.getSVal(B->getRHS()); 95 std::optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>(); 96 97 // Divide-by-undefined handled in the generic checking for uses of 98 // undefined values. 99 if (!DV) 100 return; 101 102 // Check for divide by zero. 103 ConstraintManager &CM = C.getConstraintManager(); 104 ProgramStateRef stateNotZero, stateZero; 105 std::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV); 106 107 if (!stateNotZero) { 108 assert(stateZero); 109 reportBug("Division by zero", stateZero, C); 110 return; 111 } 112 113 if ((stateNotZero && stateZero)) { 114 std::vector<SymbolRef> taintedSyms = getTaintedSymbols(C.getState(), *DV); 115 if (!taintedSyms.empty()) { 116 reportTaintBug("Division by a tainted value, possibly zero", stateZero, C, 117 taintedSyms); 118 // Fallthrough to continue analysis in case of non-zero denominator. 119 } 120 } 121 122 // If we get here, then the denom should not be zero. We abandon the implicit 123 // zero denom case for now. 124 C.addTransition(stateNotZero); 125 } 126 127 void ento::registerDivZeroChecker(CheckerManager &Mgr) { 128 Mgr.getChecker<DivZeroChecker>()->DivideZeroChecker.enable(Mgr); 129 } 130 131 bool ento::shouldRegisterDivZeroChecker(const CheckerManager &) { return true; } 132 133 void ento::registerTaintedDivChecker(CheckerManager &Mgr) { 134 Mgr.getChecker<DivZeroChecker>()->TaintedDivChecker.enable(Mgr); 135 } 136 137 bool ento::shouldRegisterTaintedDivChecker(const CheckerManager &) { 138 return true; 139 } 140