17330f729Sjoerg //== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This defines DivZeroChecker, a builtin check in ExprEngine that performs
107330f729Sjoerg // checks for division by zeros.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg
147330f729Sjoerg #include "Taint.h"
157330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
167330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
177330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
187330f729Sjoerg #include "clang/StaticAnalyzer/Core/CheckerManager.h"
197330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
207330f729Sjoerg
217330f729Sjoerg using namespace clang;
227330f729Sjoerg using namespace ento;
237330f729Sjoerg using namespace taint;
247330f729Sjoerg
257330f729Sjoerg namespace {
267330f729Sjoerg class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > {
277330f729Sjoerg mutable std::unique_ptr<BuiltinBug> BT;
287330f729Sjoerg void reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
297330f729Sjoerg std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
307330f729Sjoerg
317330f729Sjoerg public:
327330f729Sjoerg void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
337330f729Sjoerg };
347330f729Sjoerg } // end anonymous namespace
357330f729Sjoerg
getDenomExpr(const ExplodedNode * N)367330f729Sjoerg static const Expr *getDenomExpr(const ExplodedNode *N) {
377330f729Sjoerg const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
387330f729Sjoerg if (const auto *BE = dyn_cast<BinaryOperator>(S))
397330f729Sjoerg return BE->getRHS();
407330f729Sjoerg return nullptr;
417330f729Sjoerg }
427330f729Sjoerg
reportBug(const char * Msg,ProgramStateRef StateZero,CheckerContext & C,std::unique_ptr<BugReporterVisitor> Visitor) const437330f729Sjoerg void DivZeroChecker::reportBug(
447330f729Sjoerg const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
457330f729Sjoerg std::unique_ptr<BugReporterVisitor> Visitor) const {
467330f729Sjoerg if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
477330f729Sjoerg if (!BT)
487330f729Sjoerg BT.reset(new BuiltinBug(this, "Division by zero"));
497330f729Sjoerg
507330f729Sjoerg auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
517330f729Sjoerg R->addVisitor(std::move(Visitor));
527330f729Sjoerg bugreporter::trackExpressionValue(N, getDenomExpr(N), *R);
537330f729Sjoerg C.emitReport(std::move(R));
547330f729Sjoerg }
557330f729Sjoerg }
567330f729Sjoerg
checkPreStmt(const BinaryOperator * B,CheckerContext & C) const577330f729Sjoerg void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
587330f729Sjoerg CheckerContext &C) const {
597330f729Sjoerg BinaryOperator::Opcode Op = B->getOpcode();
607330f729Sjoerg if (Op != BO_Div &&
617330f729Sjoerg Op != BO_Rem &&
627330f729Sjoerg Op != BO_DivAssign &&
637330f729Sjoerg Op != BO_RemAssign)
647330f729Sjoerg return;
657330f729Sjoerg
667330f729Sjoerg if (!B->getRHS()->getType()->isScalarType())
677330f729Sjoerg return;
687330f729Sjoerg
697330f729Sjoerg SVal Denom = C.getSVal(B->getRHS());
707330f729Sjoerg Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>();
717330f729Sjoerg
727330f729Sjoerg // Divide-by-undefined handled in the generic checking for uses of
737330f729Sjoerg // undefined values.
747330f729Sjoerg if (!DV)
757330f729Sjoerg return;
767330f729Sjoerg
777330f729Sjoerg // Check for divide by zero.
787330f729Sjoerg ConstraintManager &CM = C.getConstraintManager();
797330f729Sjoerg ProgramStateRef stateNotZero, stateZero;
807330f729Sjoerg std::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV);
817330f729Sjoerg
827330f729Sjoerg if (!stateNotZero) {
837330f729Sjoerg assert(stateZero);
847330f729Sjoerg reportBug("Division by zero", stateZero, C);
857330f729Sjoerg return;
867330f729Sjoerg }
877330f729Sjoerg
887330f729Sjoerg bool TaintedD = isTainted(C.getState(), *DV);
897330f729Sjoerg if ((stateNotZero && stateZero && TaintedD)) {
907330f729Sjoerg reportBug("Division by a tainted value, possibly zero", stateZero, C,
917330f729Sjoerg std::make_unique<taint::TaintBugVisitor>(*DV));
927330f729Sjoerg return;
937330f729Sjoerg }
947330f729Sjoerg
957330f729Sjoerg // If we get here, then the denom should not be zero. We abandon the implicit
967330f729Sjoerg // zero denom case for now.
977330f729Sjoerg C.addTransition(stateNotZero);
987330f729Sjoerg }
997330f729Sjoerg
registerDivZeroChecker(CheckerManager & mgr)1007330f729Sjoerg void ento::registerDivZeroChecker(CheckerManager &mgr) {
1017330f729Sjoerg mgr.registerChecker<DivZeroChecker>();
1027330f729Sjoerg }
1037330f729Sjoerg
shouldRegisterDivZeroChecker(const CheckerManager & mgr)104*e038c9c4Sjoerg bool ento::shouldRegisterDivZeroChecker(const CheckerManager &mgr) {
1057330f729Sjoerg return true;
1067330f729Sjoerg }
107