1dc352bb8SJordan Rose //== TestAfterDivZeroChecker.cpp - Test after division by zero checker --*--==//
2dc352bb8SJordan Rose //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6dc352bb8SJordan Rose //
7dc352bb8SJordan Rose //===----------------------------------------------------------------------===//
8dc352bb8SJordan Rose //
9dc352bb8SJordan Rose // This defines TestAfterDivZeroChecker, a builtin check that performs checks
10dc352bb8SJordan Rose // for division by zero where the division occurs before comparison with zero.
11dc352bb8SJordan Rose //
12dc352bb8SJordan Rose //===----------------------------------------------------------------------===//
13dc352bb8SJordan Rose
1476a21502SKristof Umann #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15dc352bb8SJordan Rose #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16dc352bb8SJordan Rose #include "clang/StaticAnalyzer/Core/Checker.h"
17dc352bb8SJordan Rose #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
18dc352bb8SJordan Rose #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19dc352bb8SJordan Rose #include "llvm/ADT/FoldingSet.h"
20dc352bb8SJordan Rose
21dc352bb8SJordan Rose using namespace clang;
22dc352bb8SJordan Rose using namespace ento;
23dc352bb8SJordan Rose
24dc352bb8SJordan Rose namespace {
25dc352bb8SJordan Rose
26dc352bb8SJordan Rose class ZeroState {
27dc352bb8SJordan Rose private:
28dc352bb8SJordan Rose SymbolRef ZeroSymbol;
29dc352bb8SJordan Rose unsigned BlockID;
30dc352bb8SJordan Rose const StackFrameContext *SFC;
31dc352bb8SJordan Rose
32dc352bb8SJordan Rose public:
ZeroState(SymbolRef S,unsigned B,const StackFrameContext * SFC)33dc352bb8SJordan Rose ZeroState(SymbolRef S, unsigned B, const StackFrameContext *SFC)
34dc352bb8SJordan Rose : ZeroSymbol(S), BlockID(B), SFC(SFC) {}
35dc352bb8SJordan Rose
getStackFrameContext() const36dc352bb8SJordan Rose const StackFrameContext *getStackFrameContext() const { return SFC; }
37dc352bb8SJordan Rose
operator ==(const ZeroState & X) const38dc352bb8SJordan Rose bool operator==(const ZeroState &X) const {
39dc352bb8SJordan Rose return BlockID == X.BlockID && SFC == X.SFC && ZeroSymbol == X.ZeroSymbol;
40dc352bb8SJordan Rose }
41dc352bb8SJordan Rose
operator <(const ZeroState & X) const42dc352bb8SJordan Rose bool operator<(const ZeroState &X) const {
43dc352bb8SJordan Rose if (BlockID != X.BlockID)
44dc352bb8SJordan Rose return BlockID < X.BlockID;
45dc352bb8SJordan Rose if (SFC != X.SFC)
46dc352bb8SJordan Rose return SFC < X.SFC;
47dc352bb8SJordan Rose return ZeroSymbol < X.ZeroSymbol;
48dc352bb8SJordan Rose }
49dc352bb8SJordan Rose
Profile(llvm::FoldingSetNodeID & ID) const50dc352bb8SJordan Rose void Profile(llvm::FoldingSetNodeID &ID) const {
51dc352bb8SJordan Rose ID.AddInteger(BlockID);
52dc352bb8SJordan Rose ID.AddPointer(SFC);
53dc352bb8SJordan Rose ID.AddPointer(ZeroSymbol);
54dc352bb8SJordan Rose }
55dc352bb8SJordan Rose };
56dc352bb8SJordan Rose
5770ec1dd1SGeorge Karpenkov class DivisionBRVisitor : public BugReporterVisitor {
58dc352bb8SJordan Rose private:
59dc352bb8SJordan Rose SymbolRef ZeroSymbol;
60dc352bb8SJordan Rose const StackFrameContext *SFC;
615787cc2cSNAKAMURA Takumi bool Satisfied;
62dc352bb8SJordan Rose
63dc352bb8SJordan Rose public:
DivisionBRVisitor(SymbolRef ZeroSymbol,const StackFrameContext * SFC)64dc352bb8SJordan Rose DivisionBRVisitor(SymbolRef ZeroSymbol, const StackFrameContext *SFC)
655787cc2cSNAKAMURA Takumi : ZeroSymbol(ZeroSymbol), SFC(SFC), Satisfied(false) {}
66dc352bb8SJordan Rose
Profile(llvm::FoldingSetNodeID & ID) const67dc352bb8SJordan Rose void Profile(llvm::FoldingSetNodeID &ID) const override {
68dc352bb8SJordan Rose ID.Add(ZeroSymbol);
69dc352bb8SJordan Rose ID.Add(SFC);
70dc352bb8SJordan Rose }
71dc352bb8SJordan Rose
726d716ef1SKristof Umann PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ,
73dc352bb8SJordan Rose BugReporterContext &BRC,
742f169e7cSArtem Dergachev PathSensitiveBugReport &BR) override;
75dc352bb8SJordan Rose };
76dc352bb8SJordan Rose
77dc352bb8SJordan Rose class TestAfterDivZeroChecker
78dc352bb8SJordan Rose : public Checker<check::PreStmt<BinaryOperator>, check::BranchCondition,
79dc352bb8SJordan Rose check::EndFunction> {
80dc352bb8SJordan Rose mutable std::unique_ptr<BuiltinBug> DivZeroBug;
81dc352bb8SJordan Rose void reportBug(SVal Val, CheckerContext &C) const;
82dc352bb8SJordan Rose
83dc352bb8SJordan Rose public:
84dc352bb8SJordan Rose void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
85dc352bb8SJordan Rose void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const;
86ed8c05ccSReka Kovacs void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
87dc352bb8SJordan Rose void setDivZeroMap(SVal Var, CheckerContext &C) const;
88dc352bb8SJordan Rose bool hasDivZeroMap(SVal Var, const CheckerContext &C) const;
89dc352bb8SJordan Rose bool isZero(SVal S, CheckerContext &C) const;
90dc352bb8SJordan Rose };
91dc352bb8SJordan Rose } // end anonymous namespace
92dc352bb8SJordan Rose
REGISTER_SET_WITH_PROGRAMSTATE(DivZeroMap,ZeroState)93dc352bb8SJordan Rose REGISTER_SET_WITH_PROGRAMSTATE(DivZeroMap, ZeroState)
94dc352bb8SJordan Rose
952f169e7cSArtem Dergachev PathDiagnosticPieceRef
962f169e7cSArtem Dergachev DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC,
972f169e7cSArtem Dergachev PathSensitiveBugReport &BR) {
98dc352bb8SJordan Rose if (Satisfied)
99dc352bb8SJordan Rose return nullptr;
100dc352bb8SJordan Rose
101dc352bb8SJordan Rose const Expr *E = nullptr;
102dc352bb8SJordan Rose
103dc352bb8SJordan Rose if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>())
104dc352bb8SJordan Rose if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) {
105dc352bb8SJordan Rose BinaryOperator::Opcode Op = BO->getOpcode();
106dc352bb8SJordan Rose if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign ||
107dc352bb8SJordan Rose Op == BO_RemAssign) {
108dc352bb8SJordan Rose E = BO->getRHS();
109dc352bb8SJordan Rose }
110dc352bb8SJordan Rose }
111dc352bb8SJordan Rose
112dc352bb8SJordan Rose if (!E)
113dc352bb8SJordan Rose return nullptr;
114dc352bb8SJordan Rose
115d703ec94SGeorge Karpenkov SVal S = Succ->getSVal(E);
116dc352bb8SJordan Rose if (ZeroSymbol == S.getAsSymbol() && SFC == Succ->getStackFrame()) {
117dc352bb8SJordan Rose Satisfied = true;
118dc352bb8SJordan Rose
119dc352bb8SJordan Rose // Construct a new PathDiagnosticPiece.
120dc352bb8SJordan Rose ProgramPoint P = Succ->getLocation();
121dc352bb8SJordan Rose PathDiagnosticLocation L =
122dc352bb8SJordan Rose PathDiagnosticLocation::create(P, BRC.getSourceManager());
123dc352bb8SJordan Rose
124dc352bb8SJordan Rose if (!L.isValid() || !L.asLocation().isValid())
125dc352bb8SJordan Rose return nullptr;
126dc352bb8SJordan Rose
1270a0c275fSDavid Blaikie return std::make_shared<PathDiagnosticEventPiece>(
128dc352bb8SJordan Rose L, "Division with compared value made here");
129dc352bb8SJordan Rose }
130dc352bb8SJordan Rose
131dc352bb8SJordan Rose return nullptr;
132dc352bb8SJordan Rose }
133dc352bb8SJordan Rose
isZero(SVal S,CheckerContext & C) const134dc352bb8SJordan Rose bool TestAfterDivZeroChecker::isZero(SVal S, CheckerContext &C) const {
135dc352bb8SJordan Rose Optional<DefinedSVal> DSV = S.getAs<DefinedSVal>();
136dc352bb8SJordan Rose
137dc352bb8SJordan Rose if (!DSV)
138dc352bb8SJordan Rose return false;
139dc352bb8SJordan Rose
140dc352bb8SJordan Rose ConstraintManager &CM = C.getConstraintManager();
141dc352bb8SJordan Rose return !CM.assume(C.getState(), *DSV, true);
142dc352bb8SJordan Rose }
143dc352bb8SJordan Rose
setDivZeroMap(SVal Var,CheckerContext & C) const144dc352bb8SJordan Rose void TestAfterDivZeroChecker::setDivZeroMap(SVal Var, CheckerContext &C) const {
145dc352bb8SJordan Rose SymbolRef SR = Var.getAsSymbol();
146dc352bb8SJordan Rose if (!SR)
147dc352bb8SJordan Rose return;
148dc352bb8SJordan Rose
149dc352bb8SJordan Rose ProgramStateRef State = C.getState();
150dc352bb8SJordan Rose State =
151dc352bb8SJordan Rose State->add<DivZeroMap>(ZeroState(SR, C.getBlockID(), C.getStackFrame()));
152dc352bb8SJordan Rose C.addTransition(State);
153dc352bb8SJordan Rose }
154dc352bb8SJordan Rose
hasDivZeroMap(SVal Var,const CheckerContext & C) const155dc352bb8SJordan Rose bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var,
156dc352bb8SJordan Rose const CheckerContext &C) const {
157dc352bb8SJordan Rose SymbolRef SR = Var.getAsSymbol();
158dc352bb8SJordan Rose if (!SR)
159dc352bb8SJordan Rose return false;
160dc352bb8SJordan Rose
161dc352bb8SJordan Rose ZeroState ZS(SR, C.getBlockID(), C.getStackFrame());
162dc352bb8SJordan Rose return C.getState()->contains<DivZeroMap>(ZS);
163dc352bb8SJordan Rose }
164dc352bb8SJordan Rose
reportBug(SVal Val,CheckerContext & C) const165dc352bb8SJordan Rose void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const {
166e39bd407SDevin Coughlin if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
167dc352bb8SJordan Rose if (!DivZeroBug)
168dc352bb8SJordan Rose DivZeroBug.reset(new BuiltinBug(this, "Division by zero"));
169dc352bb8SJordan Rose
1702f169e7cSArtem Dergachev auto R = std::make_unique<PathSensitiveBugReport>(
1718d3a7a56SAaron Ballman *DivZeroBug, "Value being compared against zero has already been used "
1728d3a7a56SAaron Ballman "for division",
173dc352bb8SJordan Rose N);
174dc352bb8SJordan Rose
1752b3d49b6SJonas Devlieghere R->addVisitor(std::make_unique<DivisionBRVisitor>(Val.getAsSymbol(),
17691e79026SDavid Blaikie C.getStackFrame()));
1778d3a7a56SAaron Ballman C.emitReport(std::move(R));
178dc352bb8SJordan Rose }
179dc352bb8SJordan Rose }
180dc352bb8SJordan Rose
checkEndFunction(const ReturnStmt *,CheckerContext & C) const181c82d457dSGeorge Karpenkov void TestAfterDivZeroChecker::checkEndFunction(const ReturnStmt *,
182ed8c05ccSReka Kovacs CheckerContext &C) const {
183dc352bb8SJordan Rose ProgramStateRef State = C.getState();
184dc352bb8SJordan Rose
185dc352bb8SJordan Rose DivZeroMapTy DivZeroes = State->get<DivZeroMap>();
186dc352bb8SJordan Rose if (DivZeroes.isEmpty())
187dc352bb8SJordan Rose return;
188dc352bb8SJordan Rose
189dc352bb8SJordan Rose DivZeroMapTy::Factory &F = State->get_context<DivZeroMap>();
190dc352bb8SJordan Rose for (llvm::ImmutableSet<ZeroState>::iterator I = DivZeroes.begin(),
191dc352bb8SJordan Rose E = DivZeroes.end();
192dc352bb8SJordan Rose I != E; ++I) {
193dc352bb8SJordan Rose ZeroState ZS = *I;
194dc352bb8SJordan Rose if (ZS.getStackFrameContext() == C.getStackFrame())
195dc352bb8SJordan Rose DivZeroes = F.remove(DivZeroes, ZS);
196dc352bb8SJordan Rose }
197dc352bb8SJordan Rose C.addTransition(State->set<DivZeroMap>(DivZeroes));
198dc352bb8SJordan Rose }
199dc352bb8SJordan Rose
checkPreStmt(const BinaryOperator * B,CheckerContext & C) const200dc352bb8SJordan Rose void TestAfterDivZeroChecker::checkPreStmt(const BinaryOperator *B,
201dc352bb8SJordan Rose CheckerContext &C) const {
202dc352bb8SJordan Rose BinaryOperator::Opcode Op = B->getOpcode();
203dc352bb8SJordan Rose if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign ||
204dc352bb8SJordan Rose Op == BO_RemAssign) {
205dc352bb8SJordan Rose SVal S = C.getSVal(B->getRHS());
206dc352bb8SJordan Rose
207dc352bb8SJordan Rose if (!isZero(S, C))
208dc352bb8SJordan Rose setDivZeroMap(S, C);
209dc352bb8SJordan Rose }
210dc352bb8SJordan Rose }
211dc352bb8SJordan Rose
checkBranchCondition(const Stmt * Condition,CheckerContext & C) const212dc352bb8SJordan Rose void TestAfterDivZeroChecker::checkBranchCondition(const Stmt *Condition,
213dc352bb8SJordan Rose CheckerContext &C) const {
214dc352bb8SJordan Rose if (const BinaryOperator *B = dyn_cast<BinaryOperator>(Condition)) {
215dc352bb8SJordan Rose if (B->isComparisonOp()) {
216dc352bb8SJordan Rose const IntegerLiteral *IntLiteral = dyn_cast<IntegerLiteral>(B->getRHS());
217dc352bb8SJordan Rose bool LRHS = true;
218dc352bb8SJordan Rose if (!IntLiteral) {
219dc352bb8SJordan Rose IntLiteral = dyn_cast<IntegerLiteral>(B->getLHS());
220dc352bb8SJordan Rose LRHS = false;
221dc352bb8SJordan Rose }
222dc352bb8SJordan Rose
223dc352bb8SJordan Rose if (!IntLiteral || IntLiteral->getValue() != 0)
224dc352bb8SJordan Rose return;
225dc352bb8SJordan Rose
226dc352bb8SJordan Rose SVal Val = C.getSVal(LRHS ? B->getLHS() : B->getRHS());
227dc352bb8SJordan Rose if (hasDivZeroMap(Val, C))
228dc352bb8SJordan Rose reportBug(Val, C);
229dc352bb8SJordan Rose }
230dc352bb8SJordan Rose } else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(Condition)) {
231dc352bb8SJordan Rose if (U->getOpcode() == UO_LNot) {
232dc352bb8SJordan Rose SVal Val;
233dc352bb8SJordan Rose if (const ImplicitCastExpr *I =
234dc352bb8SJordan Rose dyn_cast<ImplicitCastExpr>(U->getSubExpr()))
235dc352bb8SJordan Rose Val = C.getSVal(I->getSubExpr());
236dc352bb8SJordan Rose
237dc352bb8SJordan Rose if (hasDivZeroMap(Val, C))
238dc352bb8SJordan Rose reportBug(Val, C);
239dc352bb8SJordan Rose else {
240dc352bb8SJordan Rose Val = C.getSVal(U->getSubExpr());
241dc352bb8SJordan Rose if (hasDivZeroMap(Val, C))
242dc352bb8SJordan Rose reportBug(Val, C);
243dc352bb8SJordan Rose }
244dc352bb8SJordan Rose }
245dc352bb8SJordan Rose } else if (const ImplicitCastExpr *IE =
246dc352bb8SJordan Rose dyn_cast<ImplicitCastExpr>(Condition)) {
247dc352bb8SJordan Rose SVal Val = C.getSVal(IE->getSubExpr());
248dc352bb8SJordan Rose
249dc352bb8SJordan Rose if (hasDivZeroMap(Val, C))
250dc352bb8SJordan Rose reportBug(Val, C);
251dc352bb8SJordan Rose else {
252dc352bb8SJordan Rose SVal Val = C.getSVal(Condition);
253dc352bb8SJordan Rose
254dc352bb8SJordan Rose if (hasDivZeroMap(Val, C))
255dc352bb8SJordan Rose reportBug(Val, C);
256dc352bb8SJordan Rose }
257dc352bb8SJordan Rose }
258dc352bb8SJordan Rose }
259dc352bb8SJordan Rose
registerTestAfterDivZeroChecker(CheckerManager & mgr)260dc352bb8SJordan Rose void ento::registerTestAfterDivZeroChecker(CheckerManager &mgr) {
261dc352bb8SJordan Rose mgr.registerChecker<TestAfterDivZeroChecker>();
262dc352bb8SJordan Rose }
263058a7a45SKristof Umann
shouldRegisterTestAfterDivZeroChecker(const CheckerManager & mgr)264*bda3dd0dSKirstóf Umann bool ento::shouldRegisterTestAfterDivZeroChecker(const CheckerManager &mgr) {
265058a7a45SKristof Umann return true;
266058a7a45SKristof Umann }
267