xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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