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
23using namespace clang;
24using namespace ento;
25using namespace taint;
26
27namespace {
28class 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
35public:
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
48static const Expr *getDenomExpr(const ExplodedNode *N) {
49 const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
50 if (const auto *BE = dyn_cast<BinaryOperator>(Val: S))
51 return BE->getRHS();
52 return nullptr;
53}
54
55void DivZeroChecker::reportBug(StringRef Msg, ProgramStateRef StateZero,
56 CheckerContext &C) const {
57 if (!DivideZeroChecker.isEnabled())
58 return;
59 if (ExplodedNode *N = C.generateErrorNode(State: StateZero)) {
60 auto R =
61 std::make_unique<PathSensitiveBugReport>(args: DivideZeroChecker, args&: Msg, args&: N);
62 bugreporter::trackExpressionValue(N, E: getDenomExpr(N), R&: *R);
63 C.emitReport(R: std::move(R));
64 }
65}
66
67void 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(State: StateZero)) {
73 auto R =
74 std::make_unique<PathSensitiveBugReport>(args: TaintedDivChecker, args&: Msg, args&: N);
75 bugreporter::trackExpressionValue(N, E: getDenomExpr(N), R&: *R);
76 for (auto Sym : TaintedSyms)
77 R->markInteresting(sym: Sym);
78 C.emitReport(R: std::move(R));
79 }
80}
81
82void 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(S: 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(args&: stateNotZero, args&: stateZero) = CM.assumeDual(State: C.getState(), Cond: *DV);
106
107 if (!stateNotZero) {
108 assert(stateZero);
109 reportBug(Msg: "Division by zero", StateZero: stateZero, C);
110 return;
111 }
112
113 if ((stateNotZero && stateZero)) {
114 std::vector<SymbolRef> taintedSyms = getTaintedSymbols(State: C.getState(), V: *DV);
115 if (!taintedSyms.empty()) {
116 reportTaintBug(Msg: "Division by a tainted value, possibly zero", StateZero: stateZero, C,
117 TaintedSyms: 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(State: stateNotZero);
125}
126
127void ento::registerDivZeroChecker(CheckerManager &Mgr) {
128 Mgr.getChecker<DivZeroChecker>()->DivideZeroChecker.enable(Mgr);
129}
130
131bool ento::shouldRegisterDivZeroChecker(const CheckerManager &) { return true; }
132
133void ento::registerTaintedDivChecker(CheckerManager &Mgr) {
134 Mgr.getChecker<DivZeroChecker>()->TaintedDivChecker.enable(Mgr);
135}
136
137bool ento::shouldRegisterTaintedDivChecker(const CheckerManager &) {
138 return true;
139}
140