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 mutable std::unique_ptr
<BugType
> DivZeroBug
;
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())) {
169 DivZeroBug
.reset(new BugType(this, "Division by zero"));
171 auto R
= std::make_unique
<PathSensitiveBugReport
>(
172 *DivZeroBug
, "Value being compared against zero has already been used "
176 R
->addVisitor(std::make_unique
<DivisionBRVisitor
>(Val
.getAsSymbol(),
178 C
.emitReport(std::move(R
));
182 void TestAfterDivZeroChecker::checkEndFunction(const ReturnStmt
*,
183 CheckerContext
&C
) const {
184 ProgramStateRef State
= C
.getState();
186 DivZeroMapTy DivZeroes
= State
->get
<DivZeroMap
>();
187 if (DivZeroes
.isEmpty())
190 DivZeroMapTy::Factory
&F
= State
->get_context
<DivZeroMap
>();
191 for (const ZeroState
&ZS
: DivZeroes
) {
192 if (ZS
.getStackFrameContext() == C
.getStackFrame())
193 DivZeroes
= F
.remove(DivZeroes
, ZS
);
195 C
.addTransition(State
->set
<DivZeroMap
>(DivZeroes
));
198 void TestAfterDivZeroChecker::checkPreStmt(const BinaryOperator
*B
,
199 CheckerContext
&C
) const {
200 BinaryOperator::Opcode Op
= B
->getOpcode();
201 if (Op
== BO_Div
|| Op
== BO_Rem
|| Op
== BO_DivAssign
||
202 Op
== BO_RemAssign
) {
203 SVal S
= C
.getSVal(B
->getRHS());
210 void TestAfterDivZeroChecker::checkBranchCondition(const Stmt
*Condition
,
211 CheckerContext
&C
) const {
212 if (const BinaryOperator
*B
= dyn_cast
<BinaryOperator
>(Condition
)) {
213 if (B
->isComparisonOp()) {
214 const IntegerLiteral
*IntLiteral
= dyn_cast
<IntegerLiteral
>(B
->getRHS());
217 IntLiteral
= dyn_cast
<IntegerLiteral
>(B
->getLHS());
221 if (!IntLiteral
|| IntLiteral
->getValue() != 0)
224 SVal Val
= C
.getSVal(LRHS
? B
->getLHS() : B
->getRHS());
225 if (hasDivZeroMap(Val
, C
))
228 } else if (const UnaryOperator
*U
= dyn_cast
<UnaryOperator
>(Condition
)) {
229 if (U
->getOpcode() == UO_LNot
) {
231 if (const ImplicitCastExpr
*I
=
232 dyn_cast
<ImplicitCastExpr
>(U
->getSubExpr()))
233 Val
= C
.getSVal(I
->getSubExpr());
235 if (hasDivZeroMap(Val
, C
))
238 Val
= C
.getSVal(U
->getSubExpr());
239 if (hasDivZeroMap(Val
, C
))
243 } else if (const ImplicitCastExpr
*IE
=
244 dyn_cast
<ImplicitCastExpr
>(Condition
)) {
245 SVal Val
= C
.getSVal(IE
->getSubExpr());
247 if (hasDivZeroMap(Val
, C
))
250 SVal Val
= C
.getSVal(Condition
);
252 if (hasDivZeroMap(Val
, C
))
258 void ento::registerTestAfterDivZeroChecker(CheckerManager
&mgr
) {
259 mgr
.registerChecker
<TestAfterDivZeroChecker
>();
262 bool ento::shouldRegisterTestAfterDivZeroChecker(const CheckerManager
&mgr
) {