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.
getDebugTag() const44 StringRef getDebugTag() const override { return "DivZeroChecker"; }
45 };
46 } // end anonymous namespace
47
getDenomExpr(const ExplodedNode * N)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
reportBug(StringRef Msg,ProgramStateRef StateZero,CheckerContext & C) const55 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
reportTaintBug(StringRef Msg,ProgramStateRef StateZero,CheckerContext & C,llvm::ArrayRef<SymbolRef> TaintedSyms) const67 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
checkPreStmt(const BinaryOperator * B,CheckerContext & C) const82 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
registerDivZeroChecker(CheckerManager & Mgr)127 void ento::registerDivZeroChecker(CheckerManager &Mgr) {
128 Mgr.getChecker<DivZeroChecker>()->DivideZeroChecker.enable(Mgr);
129 }
130
shouldRegisterDivZeroChecker(const CheckerManager &)131 bool ento::shouldRegisterDivZeroChecker(const CheckerManager &) { return true; }
132
registerTaintedDivChecker(CheckerManager & Mgr)133 void ento::registerTaintedDivChecker(CheckerManager &Mgr) {
134 Mgr.getChecker<DivZeroChecker>()->TaintedDivChecker.enable(Mgr);
135 }
136
shouldRegisterTaintedDivChecker(const CheckerManager &)137 bool ento::shouldRegisterTaintedDivChecker(const CheckerManager &) {
138 return true;
139 }
140