1 //== TestAfterDivZeroChecker.cpp - Test after division by zero checker --*--==//
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
7 //===----------------------------------------------------------------------===//
9 // This defines TestAfterDivZeroChecker, a builtin check that performs checks
10 // for division by zero where the division occurs before comparison with zero.
12 //===----------------------------------------------------------------------===//
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"
22 using namespace clang
;
31 const StackFrameContext
*SFC
;
34 ZeroState(SymbolRef S
, unsigned B
, const StackFrameContext
*SFC
)
35 : ZeroSymbol(S
), BlockID(B
), SFC(SFC
) {}
37 const StackFrameContext
*getStackFrameContext() const { return SFC
; }
39 bool operator==(const ZeroState
&X
) const {
40 return BlockID
== X
.BlockID
&& SFC
== X
.SFC
&& ZeroSymbol
== X
.ZeroSymbol
;
43 bool operator<(const ZeroState
&X
) const {
44 if (BlockID
!= X
.BlockID
)
45 return BlockID
< X
.BlockID
;
48 return ZeroSymbol
< X
.ZeroSymbol
;
51 void Profile(llvm::FoldingSetNodeID
&ID
) const {
52 ID
.AddInteger(BlockID
);
54 ID
.AddPointer(ZeroSymbol
);
58 class DivisionBRVisitor
: public BugReporterVisitor
{
61 const StackFrameContext
*SFC
;
65 DivisionBRVisitor(SymbolRef ZeroSymbol
, const StackFrameContext
*SFC
)
66 : ZeroSymbol(ZeroSymbol
), SFC(SFC
), Satisfied(false) {}
68 void Profile(llvm::FoldingSetNodeID
&ID
) const override
{
73 PathDiagnosticPieceRef
VisitNode(const ExplodedNode
*Succ
,
74 BugReporterContext
&BRC
,
75 PathSensitiveBugReport
&BR
) override
;
78 class TestAfterDivZeroChecker
79 : public Checker
<check::PreStmt
<BinaryOperator
>, check::BranchCondition
,
81 const BugType DivZeroBug
{this, "Division by zero"};
82 void reportBug(SVal Val
, CheckerContext
&C
) const;
85 void checkPreStmt(const BinaryOperator
*B
, CheckerContext
&C
) const;
86 void checkBranchCondition(const Stmt
*Condition
, CheckerContext
&C
) const;
87 void checkEndFunction(const ReturnStmt
*RS
, CheckerContext
&C
) const;
88 void setDivZeroMap(SVal Var
, CheckerContext
&C
) const;
89 bool hasDivZeroMap(SVal Var
, const CheckerContext
&C
) const;
90 bool isZero(SVal S
, CheckerContext
&C
) const;
92 } // end anonymous namespace
94 REGISTER_SET_WITH_PROGRAMSTATE(DivZeroMap
, ZeroState
)
96 PathDiagnosticPieceRef
97 DivisionBRVisitor::VisitNode(const ExplodedNode
*Succ
, BugReporterContext
&BRC
,
98 PathSensitiveBugReport
&BR
) {
102 const Expr
*E
= nullptr;
104 if (std::optional
<PostStmt
> P
= Succ
->getLocationAs
<PostStmt
>())
105 if (const BinaryOperator
*BO
= P
->getStmtAs
<BinaryOperator
>()) {
106 BinaryOperator::Opcode Op
= BO
->getOpcode();
107 if (Op
== BO_Div
|| Op
== BO_Rem
|| Op
== BO_DivAssign
||
108 Op
== BO_RemAssign
) {
116 SVal S
= Succ
->getSVal(E
);
117 if (ZeroSymbol
== S
.getAsSymbol() && SFC
== Succ
->getStackFrame()) {
120 // Construct a new PathDiagnosticPiece.
121 ProgramPoint P
= Succ
->getLocation();
122 PathDiagnosticLocation L
=
123 PathDiagnosticLocation::create(P
, BRC
.getSourceManager());
125 if (!L
.isValid() || !L
.asLocation().isValid())
128 return std::make_shared
<PathDiagnosticEventPiece
>(
129 L
, "Division with compared value made here");
135 bool TestAfterDivZeroChecker::isZero(SVal S
, CheckerContext
&C
) const {
136 std::optional
<DefinedSVal
> DSV
= S
.getAs
<DefinedSVal
>();
141 ConstraintManager
&CM
= C
.getConstraintManager();
142 return !CM
.assume(C
.getState(), *DSV
, true);
145 void TestAfterDivZeroChecker::setDivZeroMap(SVal Var
, CheckerContext
&C
) const {
146 SymbolRef SR
= Var
.getAsSymbol();
150 ProgramStateRef State
= C
.getState();
152 State
->add
<DivZeroMap
>(ZeroState(SR
, C
.getBlockID(), C
.getStackFrame()));
153 C
.addTransition(State
);
156 bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var
,
157 const CheckerContext
&C
) const {
158 SymbolRef SR
= Var
.getAsSymbol();
162 ZeroState
ZS(SR
, C
.getBlockID(), C
.getStackFrame());
163 return C
.getState()->contains
<DivZeroMap
>(ZS
);
166 void TestAfterDivZeroChecker::reportBug(SVal Val
, CheckerContext
&C
) const {
167 if (ExplodedNode
*N
= C
.generateErrorNode(C
.getState())) {
168 auto R
= std::make_unique
<PathSensitiveBugReport
>(
170 "Value being compared against zero has already been used "
174 R
->addVisitor(std::make_unique
<DivisionBRVisitor
>(Val
.getAsSymbol(),
176 C
.emitReport(std::move(R
));
180 void TestAfterDivZeroChecker::checkEndFunction(const ReturnStmt
*,
181 CheckerContext
&C
) const {
182 ProgramStateRef State
= C
.getState();
184 DivZeroMapTy DivZeroes
= State
->get
<DivZeroMap
>();
185 if (DivZeroes
.isEmpty())
188 DivZeroMapTy::Factory
&F
= State
->get_context
<DivZeroMap
>();
189 for (const ZeroState
&ZS
: DivZeroes
) {
190 if (ZS
.getStackFrameContext() == C
.getStackFrame())
191 DivZeroes
= F
.remove(DivZeroes
, ZS
);
193 C
.addTransition(State
->set
<DivZeroMap
>(DivZeroes
));
196 void TestAfterDivZeroChecker::checkPreStmt(const BinaryOperator
*B
,
197 CheckerContext
&C
) const {
198 BinaryOperator::Opcode Op
= B
->getOpcode();
199 if (Op
== BO_Div
|| Op
== BO_Rem
|| Op
== BO_DivAssign
||
200 Op
== BO_RemAssign
) {
201 SVal S
= C
.getSVal(B
->getRHS());
208 void TestAfterDivZeroChecker::checkBranchCondition(const Stmt
*Condition
,
209 CheckerContext
&C
) const {
210 if (const BinaryOperator
*B
= dyn_cast
<BinaryOperator
>(Condition
)) {
211 if (B
->isComparisonOp()) {
212 const IntegerLiteral
*IntLiteral
= dyn_cast
<IntegerLiteral
>(B
->getRHS());
215 IntLiteral
= dyn_cast
<IntegerLiteral
>(B
->getLHS());
219 if (!IntLiteral
|| IntLiteral
->getValue() != 0)
222 SVal Val
= C
.getSVal(LRHS
? B
->getLHS() : B
->getRHS());
223 if (hasDivZeroMap(Val
, C
))
226 } else if (const UnaryOperator
*U
= dyn_cast
<UnaryOperator
>(Condition
)) {
227 if (U
->getOpcode() == UO_LNot
) {
229 if (const ImplicitCastExpr
*I
=
230 dyn_cast
<ImplicitCastExpr
>(U
->getSubExpr()))
231 Val
= C
.getSVal(I
->getSubExpr());
233 if (hasDivZeroMap(Val
, C
))
236 Val
= C
.getSVal(U
->getSubExpr());
237 if (hasDivZeroMap(Val
, C
))
241 } else if (const ImplicitCastExpr
*IE
=
242 dyn_cast
<ImplicitCastExpr
>(Condition
)) {
243 SVal Val
= C
.getSVal(IE
->getSubExpr());
245 if (hasDivZeroMap(Val
, C
))
248 SVal Val
= C
.getSVal(Condition
);
250 if (hasDivZeroMap(Val
, C
))
256 void ento::registerTestAfterDivZeroChecker(CheckerManager
&mgr
) {
257 mgr
.registerChecker
<TestAfterDivZeroChecker
>();
260 bool ento::shouldRegisterTestAfterDivZeroChecker(const CheckerManager
&mgr
) {