diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp | 266 | 
1 files changed, 266 insertions, 0 deletions
| diff --git a/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp new file mode 100644 index 000000000000..3663b0963692 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -0,0 +1,266 @@ +//== TestAfterDivZeroChecker.cpp - Test after division by zero checker --*--==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines TestAfterDivZeroChecker, a builtin check that performs checks +//  for division by zero where the division occurs before comparison with zero. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/FoldingSet.h" + +using namespace clang; +using namespace ento; + +namespace { + +class ZeroState { +private: +  SymbolRef ZeroSymbol; +  unsigned BlockID; +  const StackFrameContext *SFC; + +public: +  ZeroState(SymbolRef S, unsigned B, const StackFrameContext *SFC) +      : ZeroSymbol(S), BlockID(B), SFC(SFC) {} + +  const StackFrameContext *getStackFrameContext() const { return SFC; } + +  bool operator==(const ZeroState &X) const { +    return BlockID == X.BlockID && SFC == X.SFC && ZeroSymbol == X.ZeroSymbol; +  } + +  bool operator<(const ZeroState &X) const { +    if (BlockID != X.BlockID) +      return BlockID < X.BlockID; +    if (SFC != X.SFC) +      return SFC < X.SFC; +    return ZeroSymbol < X.ZeroSymbol; +  } + +  void Profile(llvm::FoldingSetNodeID &ID) const { +    ID.AddInteger(BlockID); +    ID.AddPointer(SFC); +    ID.AddPointer(ZeroSymbol); +  } +}; + +class DivisionBRVisitor : public BugReporterVisitor { +private: +  SymbolRef ZeroSymbol; +  const StackFrameContext *SFC; +  bool Satisfied; + +public: +  DivisionBRVisitor(SymbolRef ZeroSymbol, const StackFrameContext *SFC) +      : ZeroSymbol(ZeroSymbol), SFC(SFC), Satisfied(false) {} + +  void Profile(llvm::FoldingSetNodeID &ID) const override { +    ID.Add(ZeroSymbol); +    ID.Add(SFC); +  } + +  PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, +                                   BugReporterContext &BRC, +                                   PathSensitiveBugReport &BR) override; +}; + +class TestAfterDivZeroChecker +    : public Checker<check::PreStmt<BinaryOperator>, check::BranchCondition, +                     check::EndFunction> { +  mutable std::unique_ptr<BuiltinBug> DivZeroBug; +  void reportBug(SVal Val, CheckerContext &C) const; + +public: +  void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; +  void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const; +  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; +  void setDivZeroMap(SVal Var, CheckerContext &C) const; +  bool hasDivZeroMap(SVal Var, const CheckerContext &C) const; +  bool isZero(SVal S, CheckerContext &C) const; +}; +} // end anonymous namespace + +REGISTER_SET_WITH_PROGRAMSTATE(DivZeroMap, ZeroState) + +PathDiagnosticPieceRef +DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, +                             PathSensitiveBugReport &BR) { +  if (Satisfied) +    return nullptr; + +  const Expr *E = nullptr; + +  if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) +    if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) { +      BinaryOperator::Opcode Op = BO->getOpcode(); +      if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign || +          Op == BO_RemAssign) { +        E = BO->getRHS(); +      } +    } + +  if (!E) +    return nullptr; + +  SVal S = Succ->getSVal(E); +  if (ZeroSymbol == S.getAsSymbol() && SFC == Succ->getStackFrame()) { +    Satisfied = true; + +    // Construct a new PathDiagnosticPiece. +    ProgramPoint P = Succ->getLocation(); +    PathDiagnosticLocation L = +        PathDiagnosticLocation::create(P, BRC.getSourceManager()); + +    if (!L.isValid() || !L.asLocation().isValid()) +      return nullptr; + +    return std::make_shared<PathDiagnosticEventPiece>( +        L, "Division with compared value made here"); +  } + +  return nullptr; +} + +bool TestAfterDivZeroChecker::isZero(SVal S, CheckerContext &C) const { +  Optional<DefinedSVal> DSV = S.getAs<DefinedSVal>(); + +  if (!DSV) +    return false; + +  ConstraintManager &CM = C.getConstraintManager(); +  return !CM.assume(C.getState(), *DSV, true); +} + +void TestAfterDivZeroChecker::setDivZeroMap(SVal Var, CheckerContext &C) const { +  SymbolRef SR = Var.getAsSymbol(); +  if (!SR) +    return; + +  ProgramStateRef State = C.getState(); +  State = +      State->add<DivZeroMap>(ZeroState(SR, C.getBlockID(), C.getStackFrame())); +  C.addTransition(State); +} + +bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var, +                                            const CheckerContext &C) const { +  SymbolRef SR = Var.getAsSymbol(); +  if (!SR) +    return false; + +  ZeroState ZS(SR, C.getBlockID(), C.getStackFrame()); +  return C.getState()->contains<DivZeroMap>(ZS); +} + +void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const { +  if (ExplodedNode *N = C.generateErrorNode(C.getState())) { +    if (!DivZeroBug) +      DivZeroBug.reset(new BuiltinBug(this, "Division by zero")); + +    auto R = std::make_unique<PathSensitiveBugReport>( +        *DivZeroBug, "Value being compared against zero has already been used " +                     "for division", +        N); + +    R->addVisitor(std::make_unique<DivisionBRVisitor>(Val.getAsSymbol(), +                                                       C.getStackFrame())); +    C.emitReport(std::move(R)); +  } +} + +void TestAfterDivZeroChecker::checkEndFunction(const ReturnStmt *, +                                               CheckerContext &C) const { +  ProgramStateRef State = C.getState(); + +  DivZeroMapTy DivZeroes = State->get<DivZeroMap>(); +  if (DivZeroes.isEmpty()) +    return; + +  DivZeroMapTy::Factory &F = State->get_context<DivZeroMap>(); +  for (llvm::ImmutableSet<ZeroState>::iterator I = DivZeroes.begin(), +                                               E = DivZeroes.end(); +       I != E; ++I) { +    ZeroState ZS = *I; +    if (ZS.getStackFrameContext() == C.getStackFrame()) +      DivZeroes = F.remove(DivZeroes, ZS); +  } +  C.addTransition(State->set<DivZeroMap>(DivZeroes)); +} + +void TestAfterDivZeroChecker::checkPreStmt(const BinaryOperator *B, +                                           CheckerContext &C) const { +  BinaryOperator::Opcode Op = B->getOpcode(); +  if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign || +      Op == BO_RemAssign) { +    SVal S = C.getSVal(B->getRHS()); + +    if (!isZero(S, C)) +      setDivZeroMap(S, C); +  } +} + +void TestAfterDivZeroChecker::checkBranchCondition(const Stmt *Condition, +                                                   CheckerContext &C) const { +  if (const BinaryOperator *B = dyn_cast<BinaryOperator>(Condition)) { +    if (B->isComparisonOp()) { +      const IntegerLiteral *IntLiteral = dyn_cast<IntegerLiteral>(B->getRHS()); +      bool LRHS = true; +      if (!IntLiteral) { +        IntLiteral = dyn_cast<IntegerLiteral>(B->getLHS()); +        LRHS = false; +      } + +      if (!IntLiteral || IntLiteral->getValue() != 0) +        return; + +      SVal Val = C.getSVal(LRHS ? B->getLHS() : B->getRHS()); +      if (hasDivZeroMap(Val, C)) +        reportBug(Val, C); +    } +  } else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(Condition)) { +    if (U->getOpcode() == UO_LNot) { +      SVal Val; +      if (const ImplicitCastExpr *I = +              dyn_cast<ImplicitCastExpr>(U->getSubExpr())) +        Val = C.getSVal(I->getSubExpr()); + +      if (hasDivZeroMap(Val, C)) +        reportBug(Val, C); +      else { +        Val = C.getSVal(U->getSubExpr()); +        if (hasDivZeroMap(Val, C)) +          reportBug(Val, C); +      } +    } +  } else if (const ImplicitCastExpr *IE = +                 dyn_cast<ImplicitCastExpr>(Condition)) { +    SVal Val = C.getSVal(IE->getSubExpr()); + +    if (hasDivZeroMap(Val, C)) +      reportBug(Val, C); +    else { +      SVal Val = C.getSVal(Condition); + +      if (hasDivZeroMap(Val, C)) +        reportBug(Val, C); +    } +  } +} + +void ento::registerTestAfterDivZeroChecker(CheckerManager &mgr) { +  mgr.registerChecker<TestAfterDivZeroChecker>(); +} + +bool ento::shouldRegisterTestAfterDivZeroChecker(const LangOptions &LO) { +  return true; +} | 
