1e5dd7070Spatrick //== TestAfterDivZeroChecker.cpp - Test after division by zero checker --*--==//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This defines TestAfterDivZeroChecker, a builtin check that performs checks
10e5dd7070Spatrick // for division by zero where the division occurs before comparison with zero.
11e5dd7070Spatrick //
12e5dd7070Spatrick //===----------------------------------------------------------------------===//
13e5dd7070Spatrick
14e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
17e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19e5dd7070Spatrick #include "llvm/ADT/FoldingSet.h"
20*12c85518Srobert #include <optional>
21e5dd7070Spatrick
22e5dd7070Spatrick using namespace clang;
23e5dd7070Spatrick using namespace ento;
24e5dd7070Spatrick
25e5dd7070Spatrick namespace {
26e5dd7070Spatrick
27e5dd7070Spatrick class ZeroState {
28e5dd7070Spatrick private:
29e5dd7070Spatrick SymbolRef ZeroSymbol;
30e5dd7070Spatrick unsigned BlockID;
31e5dd7070Spatrick const StackFrameContext *SFC;
32e5dd7070Spatrick
33e5dd7070Spatrick public:
ZeroState(SymbolRef S,unsigned B,const StackFrameContext * SFC)34e5dd7070Spatrick ZeroState(SymbolRef S, unsigned B, const StackFrameContext *SFC)
35e5dd7070Spatrick : ZeroSymbol(S), BlockID(B), SFC(SFC) {}
36e5dd7070Spatrick
getStackFrameContext() const37e5dd7070Spatrick const StackFrameContext *getStackFrameContext() const { return SFC; }
38e5dd7070Spatrick
operator ==(const ZeroState & X) const39e5dd7070Spatrick bool operator==(const ZeroState &X) const {
40e5dd7070Spatrick return BlockID == X.BlockID && SFC == X.SFC && ZeroSymbol == X.ZeroSymbol;
41e5dd7070Spatrick }
42e5dd7070Spatrick
operator <(const ZeroState & X) const43e5dd7070Spatrick bool operator<(const ZeroState &X) const {
44e5dd7070Spatrick if (BlockID != X.BlockID)
45e5dd7070Spatrick return BlockID < X.BlockID;
46e5dd7070Spatrick if (SFC != X.SFC)
47e5dd7070Spatrick return SFC < X.SFC;
48e5dd7070Spatrick return ZeroSymbol < X.ZeroSymbol;
49e5dd7070Spatrick }
50e5dd7070Spatrick
Profile(llvm::FoldingSetNodeID & ID) const51e5dd7070Spatrick void Profile(llvm::FoldingSetNodeID &ID) const {
52e5dd7070Spatrick ID.AddInteger(BlockID);
53e5dd7070Spatrick ID.AddPointer(SFC);
54e5dd7070Spatrick ID.AddPointer(ZeroSymbol);
55e5dd7070Spatrick }
56e5dd7070Spatrick };
57e5dd7070Spatrick
58e5dd7070Spatrick class DivisionBRVisitor : public BugReporterVisitor {
59e5dd7070Spatrick private:
60e5dd7070Spatrick SymbolRef ZeroSymbol;
61e5dd7070Spatrick const StackFrameContext *SFC;
62e5dd7070Spatrick bool Satisfied;
63e5dd7070Spatrick
64e5dd7070Spatrick public:
DivisionBRVisitor(SymbolRef ZeroSymbol,const StackFrameContext * SFC)65e5dd7070Spatrick DivisionBRVisitor(SymbolRef ZeroSymbol, const StackFrameContext *SFC)
66e5dd7070Spatrick : ZeroSymbol(ZeroSymbol), SFC(SFC), Satisfied(false) {}
67e5dd7070Spatrick
Profile(llvm::FoldingSetNodeID & ID) const68e5dd7070Spatrick void Profile(llvm::FoldingSetNodeID &ID) const override {
69e5dd7070Spatrick ID.Add(ZeroSymbol);
70e5dd7070Spatrick ID.Add(SFC);
71e5dd7070Spatrick }
72e5dd7070Spatrick
73e5dd7070Spatrick PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
74e5dd7070Spatrick BugReporterContext &BRC,
75e5dd7070Spatrick PathSensitiveBugReport &BR) override;
76e5dd7070Spatrick };
77e5dd7070Spatrick
78e5dd7070Spatrick class TestAfterDivZeroChecker
79e5dd7070Spatrick : public Checker<check::PreStmt<BinaryOperator>, check::BranchCondition,
80e5dd7070Spatrick check::EndFunction> {
81e5dd7070Spatrick mutable std::unique_ptr<BuiltinBug> DivZeroBug;
82e5dd7070Spatrick void reportBug(SVal Val, CheckerContext &C) const;
83e5dd7070Spatrick
84e5dd7070Spatrick public:
85e5dd7070Spatrick void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
86e5dd7070Spatrick void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const;
87e5dd7070Spatrick void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
88e5dd7070Spatrick void setDivZeroMap(SVal Var, CheckerContext &C) const;
89e5dd7070Spatrick bool hasDivZeroMap(SVal Var, const CheckerContext &C) const;
90e5dd7070Spatrick bool isZero(SVal S, CheckerContext &C) const;
91e5dd7070Spatrick };
92e5dd7070Spatrick } // end anonymous namespace
93e5dd7070Spatrick
REGISTER_SET_WITH_PROGRAMSTATE(DivZeroMap,ZeroState)94e5dd7070Spatrick REGISTER_SET_WITH_PROGRAMSTATE(DivZeroMap, ZeroState)
95e5dd7070Spatrick
96e5dd7070Spatrick PathDiagnosticPieceRef
97e5dd7070Spatrick DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC,
98e5dd7070Spatrick PathSensitiveBugReport &BR) {
99e5dd7070Spatrick if (Satisfied)
100e5dd7070Spatrick return nullptr;
101e5dd7070Spatrick
102e5dd7070Spatrick const Expr *E = nullptr;
103e5dd7070Spatrick
104*12c85518Srobert if (std::optional<PostStmt> P = Succ->getLocationAs<PostStmt>())
105e5dd7070Spatrick if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) {
106e5dd7070Spatrick BinaryOperator::Opcode Op = BO->getOpcode();
107e5dd7070Spatrick if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign ||
108e5dd7070Spatrick Op == BO_RemAssign) {
109e5dd7070Spatrick E = BO->getRHS();
110e5dd7070Spatrick }
111e5dd7070Spatrick }
112e5dd7070Spatrick
113e5dd7070Spatrick if (!E)
114e5dd7070Spatrick return nullptr;
115e5dd7070Spatrick
116e5dd7070Spatrick SVal S = Succ->getSVal(E);
117e5dd7070Spatrick if (ZeroSymbol == S.getAsSymbol() && SFC == Succ->getStackFrame()) {
118e5dd7070Spatrick Satisfied = true;
119e5dd7070Spatrick
120e5dd7070Spatrick // Construct a new PathDiagnosticPiece.
121e5dd7070Spatrick ProgramPoint P = Succ->getLocation();
122e5dd7070Spatrick PathDiagnosticLocation L =
123e5dd7070Spatrick PathDiagnosticLocation::create(P, BRC.getSourceManager());
124e5dd7070Spatrick
125e5dd7070Spatrick if (!L.isValid() || !L.asLocation().isValid())
126e5dd7070Spatrick return nullptr;
127e5dd7070Spatrick
128e5dd7070Spatrick return std::make_shared<PathDiagnosticEventPiece>(
129e5dd7070Spatrick L, "Division with compared value made here");
130e5dd7070Spatrick }
131e5dd7070Spatrick
132e5dd7070Spatrick return nullptr;
133e5dd7070Spatrick }
134e5dd7070Spatrick
isZero(SVal S,CheckerContext & C) const135e5dd7070Spatrick bool TestAfterDivZeroChecker::isZero(SVal S, CheckerContext &C) const {
136*12c85518Srobert std::optional<DefinedSVal> DSV = S.getAs<DefinedSVal>();
137e5dd7070Spatrick
138e5dd7070Spatrick if (!DSV)
139e5dd7070Spatrick return false;
140e5dd7070Spatrick
141e5dd7070Spatrick ConstraintManager &CM = C.getConstraintManager();
142e5dd7070Spatrick return !CM.assume(C.getState(), *DSV, true);
143e5dd7070Spatrick }
144e5dd7070Spatrick
setDivZeroMap(SVal Var,CheckerContext & C) const145e5dd7070Spatrick void TestAfterDivZeroChecker::setDivZeroMap(SVal Var, CheckerContext &C) const {
146e5dd7070Spatrick SymbolRef SR = Var.getAsSymbol();
147e5dd7070Spatrick if (!SR)
148e5dd7070Spatrick return;
149e5dd7070Spatrick
150e5dd7070Spatrick ProgramStateRef State = C.getState();
151e5dd7070Spatrick State =
152e5dd7070Spatrick State->add<DivZeroMap>(ZeroState(SR, C.getBlockID(), C.getStackFrame()));
153e5dd7070Spatrick C.addTransition(State);
154e5dd7070Spatrick }
155e5dd7070Spatrick
hasDivZeroMap(SVal Var,const CheckerContext & C) const156e5dd7070Spatrick bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var,
157e5dd7070Spatrick const CheckerContext &C) const {
158e5dd7070Spatrick SymbolRef SR = Var.getAsSymbol();
159e5dd7070Spatrick if (!SR)
160e5dd7070Spatrick return false;
161e5dd7070Spatrick
162e5dd7070Spatrick ZeroState ZS(SR, C.getBlockID(), C.getStackFrame());
163e5dd7070Spatrick return C.getState()->contains<DivZeroMap>(ZS);
164e5dd7070Spatrick }
165e5dd7070Spatrick
reportBug(SVal Val,CheckerContext & C) const166e5dd7070Spatrick void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const {
167e5dd7070Spatrick if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
168e5dd7070Spatrick if (!DivZeroBug)
169e5dd7070Spatrick DivZeroBug.reset(new BuiltinBug(this, "Division by zero"));
170e5dd7070Spatrick
171e5dd7070Spatrick auto R = std::make_unique<PathSensitiveBugReport>(
172e5dd7070Spatrick *DivZeroBug, "Value being compared against zero has already been used "
173e5dd7070Spatrick "for division",
174e5dd7070Spatrick N);
175e5dd7070Spatrick
176e5dd7070Spatrick R->addVisitor(std::make_unique<DivisionBRVisitor>(Val.getAsSymbol(),
177e5dd7070Spatrick C.getStackFrame()));
178e5dd7070Spatrick C.emitReport(std::move(R));
179e5dd7070Spatrick }
180e5dd7070Spatrick }
181e5dd7070Spatrick
checkEndFunction(const ReturnStmt *,CheckerContext & C) const182e5dd7070Spatrick void TestAfterDivZeroChecker::checkEndFunction(const ReturnStmt *,
183e5dd7070Spatrick CheckerContext &C) const {
184e5dd7070Spatrick ProgramStateRef State = C.getState();
185e5dd7070Spatrick
186e5dd7070Spatrick DivZeroMapTy DivZeroes = State->get<DivZeroMap>();
187e5dd7070Spatrick if (DivZeroes.isEmpty())
188e5dd7070Spatrick return;
189e5dd7070Spatrick
190e5dd7070Spatrick DivZeroMapTy::Factory &F = State->get_context<DivZeroMap>();
191e5dd7070Spatrick for (llvm::ImmutableSet<ZeroState>::iterator I = DivZeroes.begin(),
192e5dd7070Spatrick E = DivZeroes.end();
193e5dd7070Spatrick I != E; ++I) {
194e5dd7070Spatrick ZeroState ZS = *I;
195e5dd7070Spatrick if (ZS.getStackFrameContext() == C.getStackFrame())
196e5dd7070Spatrick DivZeroes = F.remove(DivZeroes, ZS);
197e5dd7070Spatrick }
198e5dd7070Spatrick C.addTransition(State->set<DivZeroMap>(DivZeroes));
199e5dd7070Spatrick }
200e5dd7070Spatrick
checkPreStmt(const BinaryOperator * B,CheckerContext & C) const201e5dd7070Spatrick void TestAfterDivZeroChecker::checkPreStmt(const BinaryOperator *B,
202e5dd7070Spatrick CheckerContext &C) const {
203e5dd7070Spatrick BinaryOperator::Opcode Op = B->getOpcode();
204e5dd7070Spatrick if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign ||
205e5dd7070Spatrick Op == BO_RemAssign) {
206e5dd7070Spatrick SVal S = C.getSVal(B->getRHS());
207e5dd7070Spatrick
208e5dd7070Spatrick if (!isZero(S, C))
209e5dd7070Spatrick setDivZeroMap(S, C);
210e5dd7070Spatrick }
211e5dd7070Spatrick }
212e5dd7070Spatrick
checkBranchCondition(const Stmt * Condition,CheckerContext & C) const213e5dd7070Spatrick void TestAfterDivZeroChecker::checkBranchCondition(const Stmt *Condition,
214e5dd7070Spatrick CheckerContext &C) const {
215e5dd7070Spatrick if (const BinaryOperator *B = dyn_cast<BinaryOperator>(Condition)) {
216e5dd7070Spatrick if (B->isComparisonOp()) {
217e5dd7070Spatrick const IntegerLiteral *IntLiteral = dyn_cast<IntegerLiteral>(B->getRHS());
218e5dd7070Spatrick bool LRHS = true;
219e5dd7070Spatrick if (!IntLiteral) {
220e5dd7070Spatrick IntLiteral = dyn_cast<IntegerLiteral>(B->getLHS());
221e5dd7070Spatrick LRHS = false;
222e5dd7070Spatrick }
223e5dd7070Spatrick
224e5dd7070Spatrick if (!IntLiteral || IntLiteral->getValue() != 0)
225e5dd7070Spatrick return;
226e5dd7070Spatrick
227e5dd7070Spatrick SVal Val = C.getSVal(LRHS ? B->getLHS() : B->getRHS());
228e5dd7070Spatrick if (hasDivZeroMap(Val, C))
229e5dd7070Spatrick reportBug(Val, C);
230e5dd7070Spatrick }
231e5dd7070Spatrick } else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(Condition)) {
232e5dd7070Spatrick if (U->getOpcode() == UO_LNot) {
233e5dd7070Spatrick SVal Val;
234e5dd7070Spatrick if (const ImplicitCastExpr *I =
235e5dd7070Spatrick dyn_cast<ImplicitCastExpr>(U->getSubExpr()))
236e5dd7070Spatrick Val = C.getSVal(I->getSubExpr());
237e5dd7070Spatrick
238e5dd7070Spatrick if (hasDivZeroMap(Val, C))
239e5dd7070Spatrick reportBug(Val, C);
240e5dd7070Spatrick else {
241e5dd7070Spatrick Val = C.getSVal(U->getSubExpr());
242e5dd7070Spatrick if (hasDivZeroMap(Val, C))
243e5dd7070Spatrick reportBug(Val, C);
244e5dd7070Spatrick }
245e5dd7070Spatrick }
246e5dd7070Spatrick } else if (const ImplicitCastExpr *IE =
247e5dd7070Spatrick dyn_cast<ImplicitCastExpr>(Condition)) {
248e5dd7070Spatrick SVal Val = C.getSVal(IE->getSubExpr());
249e5dd7070Spatrick
250e5dd7070Spatrick if (hasDivZeroMap(Val, C))
251e5dd7070Spatrick reportBug(Val, C);
252e5dd7070Spatrick else {
253e5dd7070Spatrick SVal Val = C.getSVal(Condition);
254e5dd7070Spatrick
255e5dd7070Spatrick if (hasDivZeroMap(Val, C))
256e5dd7070Spatrick reportBug(Val, C);
257e5dd7070Spatrick }
258e5dd7070Spatrick }
259e5dd7070Spatrick }
260e5dd7070Spatrick
registerTestAfterDivZeroChecker(CheckerManager & mgr)261e5dd7070Spatrick void ento::registerTestAfterDivZeroChecker(CheckerManager &mgr) {
262e5dd7070Spatrick mgr.registerChecker<TestAfterDivZeroChecker>();
263e5dd7070Spatrick }
264e5dd7070Spatrick
shouldRegisterTestAfterDivZeroChecker(const CheckerManager & mgr)265ec727ea7Spatrick bool ento::shouldRegisterTestAfterDivZeroChecker(const CheckerManager &mgr) {
266e5dd7070Spatrick return true;
267e5dd7070Spatrick }
268