clang 20.0.0git
DivZeroChecker.cpp
Go to the documentation of this file.
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
21#include <optional>
22
23using namespace clang;
24using namespace ento;
25using namespace taint;
26
27namespace {
28class DivZeroChecker : public Checker<check::PreStmt<BinaryOperator>> {
29 void reportBug(StringRef Msg, ProgramStateRef StateZero,
30 CheckerContext &C) const;
31 void reportTaintBug(StringRef Msg, ProgramStateRef StateZero,
33 llvm::ArrayRef<SymbolRef> TaintedSyms) const;
34
35public:
36 /// This checker class implements several user facing checkers
37 enum CheckKind { CK_DivideZero, CK_TaintedDivChecker, CK_NumCheckKinds };
38 bool ChecksEnabled[CK_NumCheckKinds] = {false};
39 CheckerNameRef CheckNames[CK_NumCheckKinds];
40 mutable std::unique_ptr<BugType> BugTypes[CK_NumCheckKinds];
41
42 void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
43};
44} // end anonymous namespace
45
46static const Expr *getDenomExpr(const ExplodedNode *N) {
47 const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
48 if (const auto *BE = dyn_cast<BinaryOperator>(S))
49 return BE->getRHS();
50 return nullptr;
51}
52
53void DivZeroChecker::reportBug(StringRef Msg, ProgramStateRef StateZero,
54 CheckerContext &C) const {
55 if (!ChecksEnabled[CK_DivideZero])
56 return;
57 if (!BugTypes[CK_DivideZero])
58 BugTypes[CK_DivideZero].reset(
59 new BugType(CheckNames[CK_DivideZero], "Division by zero"));
60 if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
61 auto R = std::make_unique<PathSensitiveBugReport>(*BugTypes[CK_DivideZero],
62 Msg, N);
64 C.emitReport(std::move(R));
65 }
66}
67
68void DivZeroChecker::reportTaintBug(
69 StringRef Msg, ProgramStateRef StateZero, CheckerContext &C,
70 llvm::ArrayRef<SymbolRef> TaintedSyms) const {
71 if (!ChecksEnabled[CK_TaintedDivChecker])
72 return;
73 if (!BugTypes[CK_TaintedDivChecker])
74 BugTypes[CK_TaintedDivChecker].reset(
75 new BugType(CheckNames[CK_TaintedDivChecker], "Division by zero",
77 if (ExplodedNode *N = C.generateNonFatalErrorNode(StateZero)) {
78 auto R = std::make_unique<PathSensitiveBugReport>(
79 *BugTypes[CK_TaintedDivChecker], Msg, N);
81 for (auto Sym : TaintedSyms)
82 R->markInteresting(Sym);
83 C.emitReport(std::move(R));
84 }
85}
86
87void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
88 CheckerContext &C) const {
90 if (Op != BO_Div &&
91 Op != BO_Rem &&
92 Op != BO_DivAssign &&
93 Op != BO_RemAssign)
94 return;
95
96 if (!B->getRHS()->getType()->isScalarType())
97 return;
98
99 SVal Denom = C.getSVal(B->getRHS());
100 std::optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>();
101
102 // Divide-by-undefined handled in the generic checking for uses of
103 // undefined values.
104 if (!DV)
105 return;
106
107 // Check for divide by zero.
108 ConstraintManager &CM = C.getConstraintManager();
109 ProgramStateRef stateNotZero, stateZero;
110 std::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV);
111
112 if (!stateNotZero) {
113 assert(stateZero);
114 reportBug("Division by zero", stateZero, C);
115 return;
116 }
117
118 if ((stateNotZero && stateZero)) {
119 std::vector<SymbolRef> taintedSyms = getTaintedSymbols(C.getState(), *DV);
120 if (!taintedSyms.empty()) {
121 reportTaintBug("Division by a tainted value, possibly zero", stateNotZero,
122 C, taintedSyms);
123 return;
124 }
125 }
126
127 // If we get here, then the denom should not be zero. We abandon the implicit
128 // zero denom case for now.
129 C.addTransition(stateNotZero);
130}
131
132void ento::registerDivZeroChecker(CheckerManager &mgr) {
133 DivZeroChecker *checker = mgr.registerChecker<DivZeroChecker>();
134 checker->ChecksEnabled[DivZeroChecker::CK_DivideZero] = true;
135 checker->CheckNames[DivZeroChecker::CK_DivideZero] =
137}
138
139bool ento::shouldRegisterDivZeroChecker(const CheckerManager &mgr) {
140 return true;
141}
142
143void ento::registerTaintedDivChecker(CheckerManager &mgr) {
144 DivZeroChecker *checker;
145 if (!mgr.isRegisteredChecker<DivZeroChecker>())
146 checker = mgr.registerChecker<DivZeroChecker>();
147 else
148 checker = mgr.getChecker<DivZeroChecker>();
149 checker->ChecksEnabled[DivZeroChecker::CK_TaintedDivChecker] = true;
150 checker->CheckNames[DivZeroChecker::CK_TaintedDivChecker] =
152}
153
154bool ento::shouldRegisterTaintedDivChecker(const CheckerManager &mgr) {
155 return true;
156}
static const Expr * getDenomExpr(const ExplodedNode *N)
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3909
Expr * getRHS() const
Definition: Expr.h:3961
Opcode getOpcode() const
Definition: Expr.h:3954
This represents one expression.
Definition: Expr.h:110
QualType getType() const
Definition: Expr.h:142
Stmt - This represents one statement.
Definition: Stmt.h:84
bool isScalarType() const
Definition: Type.h:8609
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
CheckerNameRef getCurrentCheckerName() const
This wrapper is used to ensure that only StringRefs originating from the CheckerRegistry are used as ...
ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond)
Returns a pair of states (StTrue, StFalse) where the given condition is assumed to be true or false,...
std::optional< T > getLocationAs() const &
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:56
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
Definition: SVals.h:87
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
std::vector< SymbolRef > getTaintedSymbols(ProgramStateRef State, const Stmt *S, const LocationContext *LCtx, TaintTagType Kind=TaintTagGeneric)
Returns the tainted Symbols for a given Statement and state.
Definition: Taint.cpp:170
The JSON file list parser is used to communicate input to InstallAPI.
BinaryOperatorKind