xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp (revision a90b9d0159070121c221b966469c3e36d912bf82)
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 Checker< check::PreStmt<BinaryOperator> > {
29   const BugType BT{this, "Division by zero"};
30   const BugType TaintBT{this, "Division by zero", categories::TaintedData};
31   void reportBug(StringRef Msg, ProgramStateRef StateZero,
32                  CheckerContext &C) const;
33   void reportTaintBug(StringRef Msg, ProgramStateRef StateZero,
34                       CheckerContext &C,
35                       llvm::ArrayRef<SymbolRef> TaintedSyms) const;
36 
37 public:
38   void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
39 };
40 } // end anonymous namespace
41 
42 static const Expr *getDenomExpr(const ExplodedNode *N) {
43   const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
44   if (const auto *BE = dyn_cast<BinaryOperator>(S))
45     return BE->getRHS();
46   return nullptr;
47 }
48 
49 void DivZeroChecker::reportBug(StringRef Msg, ProgramStateRef StateZero,
50                                CheckerContext &C) const {
51   if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
52     auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
53     bugreporter::trackExpressionValue(N, getDenomExpr(N), *R);
54     C.emitReport(std::move(R));
55   }
56 }
57 
58 void DivZeroChecker::reportTaintBug(
59     StringRef Msg, ProgramStateRef StateZero, CheckerContext &C,
60     llvm::ArrayRef<SymbolRef> TaintedSyms) const {
61   if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
62     auto R = std::make_unique<PathSensitiveBugReport>(TaintBT, Msg, N);
63     bugreporter::trackExpressionValue(N, getDenomExpr(N), *R);
64     for (auto Sym : TaintedSyms)
65       R->markInteresting(Sym);
66     C.emitReport(std::move(R));
67   }
68 }
69 
70 void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
71                                   CheckerContext &C) const {
72   BinaryOperator::Opcode Op = B->getOpcode();
73   if (Op != BO_Div &&
74       Op != BO_Rem &&
75       Op != BO_DivAssign &&
76       Op != BO_RemAssign)
77     return;
78 
79   if (!B->getRHS()->getType()->isScalarType())
80     return;
81 
82   SVal Denom = C.getSVal(B->getRHS());
83   std::optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>();
84 
85   // Divide-by-undefined handled in the generic checking for uses of
86   // undefined values.
87   if (!DV)
88     return;
89 
90   // Check for divide by zero.
91   ConstraintManager &CM = C.getConstraintManager();
92   ProgramStateRef stateNotZero, stateZero;
93   std::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV);
94 
95   if (!stateNotZero) {
96     assert(stateZero);
97     reportBug("Division by zero", stateZero, C);
98     return;
99   }
100 
101   if ((stateNotZero && stateZero)) {
102     std::vector<SymbolRef> taintedSyms = getTaintedSymbols(C.getState(), *DV);
103     if (!taintedSyms.empty()) {
104       reportTaintBug("Division by a tainted value, possibly zero", stateZero, C,
105                      taintedSyms);
106       return;
107     }
108   }
109 
110   // If we get here, then the denom should not be zero. We abandon the implicit
111   // zero denom case for now.
112   C.addTransition(stateNotZero);
113 }
114 
115 void ento::registerDivZeroChecker(CheckerManager &mgr) {
116   mgr.registerChecker<DivZeroChecker>();
117 }
118 
119 bool ento::shouldRegisterDivZeroChecker(const CheckerManager &mgr) {
120   return true;
121 }
122