Clang Project

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