brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.3 KiB · 4682b67 Raw
250 lines · cpp
1//=== ErrnoChecker.cpp ------------------------------------------*- C++ -*-===//2//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-exception6//7//===----------------------------------------------------------------------===//8//9// This defines an "errno checker" that can detect some invalid use of the10// system-defined value 'errno'. This checker works together with the11// ErrnoModeling checker and other checkers like StdCLibraryFunctions.12//13//===----------------------------------------------------------------------===//14 15#include "ErrnoModeling.h"16#include "clang/AST/ParentMapContext.h"17#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"18#include "clang/StaticAnalyzer/Core/Checker.h"19#include "clang/StaticAnalyzer/Core/CheckerManager.h"20#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"22#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"23#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"24#include "llvm/ADT/STLExtras.h"25#include <optional>26 27using namespace clang;28using namespace ento;29using namespace errno_modeling;30 31namespace {32 33class ErrnoChecker34    : public Checker<check::Location, check::PreCall, check::RegionChanges> {35public:36  void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,37                     CheckerContext &) const;38  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;39  ProgramStateRef40  checkRegionChanges(ProgramStateRef State,41                     const InvalidatedSymbols *Invalidated,42                     ArrayRef<const MemRegion *> ExplicitRegions,43                     ArrayRef<const MemRegion *> Regions,44                     const LocationContext *LCtx, const CallEvent *Call) const;45 46  /// Indicates if a read (load) of \c errno is allowed in a non-condition part47  /// of \c if, \c switch, loop and conditional statements when the errno48  /// value may be undefined.49  bool AllowErrnoReadOutsideConditions = true;50 51private:52  void generateErrnoNotCheckedBug(CheckerContext &C, ProgramStateRef State,53                                  const MemRegion *ErrnoRegion,54                                  const CallEvent *CallMayChangeErrno) const;55 56  BugType BT_InvalidErrnoRead{this, "Value of 'errno' could be undefined",57                              "Error handling"};58  BugType BT_ErrnoNotChecked{this, "Value of 'errno' was not checked",59                             "Error handling"};60};61 62} // namespace63 64static ProgramStateRef setErrnoStateIrrelevant(ProgramStateRef State) {65  return setErrnoState(State, Irrelevant);66}67 68/// Check if a statement (expression) or an ancestor of it is in a condition69/// part of a (conditional, loop, switch) statement.70static bool isInCondition(const Stmt *S, CheckerContext &C) {71  ParentMapContext &ParentCtx = C.getASTContext().getParentMapContext();72  bool CondFound = false;73  while (S && !CondFound) {74    const DynTypedNodeList Parents = ParentCtx.getParents(*S);75    if (Parents.empty())76      break;77    const auto *ParentS = Parents[0].get<Stmt>();78    if (!ParentS || isa<CallExpr>(ParentS))79      break;80    switch (ParentS->getStmtClass()) {81    case Expr::IfStmtClass:82      CondFound = (S == cast<IfStmt>(ParentS)->getCond());83      break;84    case Expr::ForStmtClass:85      CondFound = (S == cast<ForStmt>(ParentS)->getCond());86      break;87    case Expr::DoStmtClass:88      CondFound = (S == cast<DoStmt>(ParentS)->getCond());89      break;90    case Expr::WhileStmtClass:91      CondFound = (S == cast<WhileStmt>(ParentS)->getCond());92      break;93    case Expr::SwitchStmtClass:94      CondFound = (S == cast<SwitchStmt>(ParentS)->getCond());95      break;96    case Expr::ConditionalOperatorClass:97      CondFound = (S == cast<ConditionalOperator>(ParentS)->getCond());98      break;99    case Expr::BinaryConditionalOperatorClass:100      CondFound = (S == cast<BinaryConditionalOperator>(ParentS)->getCommon());101      break;102    default:103      break;104    }105    S = ParentS;106  }107  return CondFound;108}109 110void ErrnoChecker::generateErrnoNotCheckedBug(111    CheckerContext &C, ProgramStateRef State, const MemRegion *ErrnoRegion,112    const CallEvent *CallMayChangeErrno) const {113  if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {114    SmallString<100> StrBuf;115    llvm::raw_svector_ostream OS(StrBuf);116    if (CallMayChangeErrno) {117      OS << "Value of 'errno' was not checked and may be overwritten by "118            "function '";119      const auto *CallD =120          dyn_cast_or_null<FunctionDecl>(CallMayChangeErrno->getDecl());121      assert(CallD && CallD->getIdentifier());122      OS << CallD->getIdentifier()->getName() << "'";123    } else {124      OS << "Value of 'errno' was not checked and is overwritten here";125    }126    auto BR = std::make_unique<PathSensitiveBugReport>(BT_ErrnoNotChecked,127                                                       OS.str(), N);128    BR->markInteresting(ErrnoRegion);129    C.emitReport(std::move(BR));130  }131}132 133void ErrnoChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S,134                                 CheckerContext &C) const {135  std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());136  if (!ErrnoLoc)137    return;138 139  auto L = Loc.getAs<ento::Loc>();140  if (!L || *ErrnoLoc != *L)141    return;142 143  ProgramStateRef State = C.getState();144  ErrnoCheckState EState = getErrnoState(State);145 146  if (IsLoad) {147    switch (EState) {148    case MustNotBeChecked:149      // Read of 'errno' when it may have undefined value.150      if (!AllowErrnoReadOutsideConditions || isInCondition(S, C)) {151        if (ExplodedNode *N = C.generateErrorNode()) {152          auto BR = std::make_unique<PathSensitiveBugReport>(153              BT_InvalidErrnoRead,154              "An undefined value may be read from 'errno'", N);155          BR->markInteresting(ErrnoLoc->getAsRegion());156          C.emitReport(std::move(BR));157        }158      }159      break;160    case MustBeChecked:161      // 'errno' has to be checked. A load is required for this, with no more162      // information we can assume that it is checked somehow.163      // After this place 'errno' is allowed to be read and written.164      State = setErrnoStateIrrelevant(State);165      C.addTransition(State);166      break;167    default:168      break;169    }170  } else {171    switch (EState) {172    case MustBeChecked:173      // 'errno' is overwritten without a read before but it should have been174      // checked.175      generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(State),176                                 ErrnoLoc->getAsRegion(), nullptr);177      break;178    case MustNotBeChecked:179      // Write to 'errno' when it is not allowed to be read.180      // After this place 'errno' is allowed to be read and written.181      State = setErrnoStateIrrelevant(State);182      C.addTransition(State);183      break;184    default:185      break;186    }187  }188}189 190void ErrnoChecker::checkPreCall(const CallEvent &Call,191                                CheckerContext &C) const {192  const auto *CallF = dyn_cast_or_null<FunctionDecl>(Call.getDecl());193  if (!CallF)194    return;195 196  CallF = CallF->getCanonicalDecl();197  // If 'errno' must be checked, it should be done as soon as possible, and198  // before any other call to a system function (something in a system header).199  // To avoid use of a long list of functions that may change 'errno'200  // (which may be different with standard library versions) assume that any201  // function can change it.202  // A list of special functions can be used that are allowed here without203  // generation of diagnostic. For now the only such case is 'errno' itself.204  // Probably 'strerror'?205  if (CallF->isExternC() && CallF->isGlobal() &&206      C.getSourceManager().isInSystemHeader(CallF->getLocation()) &&207      !isErrnoLocationCall(Call)) {208    if (getErrnoState(C.getState()) == MustBeChecked) {209      std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(C.getState());210      assert(ErrnoLoc && "ErrnoLoc should exist if an errno state is set.");211      generateErrnoNotCheckedBug(C, setErrnoStateIrrelevant(C.getState()),212                                 ErrnoLoc->getAsRegion(), &Call);213    }214  }215}216 217ProgramStateRef ErrnoChecker::checkRegionChanges(218    ProgramStateRef State, const InvalidatedSymbols *Invalidated,219    ArrayRef<const MemRegion *> ExplicitRegions,220    ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,221    const CallEvent *Call) const {222  std::optional<ento::Loc> ErrnoLoc = getErrnoLoc(State);223  if (!ErrnoLoc)224    return State;225  const MemRegion *ErrnoRegion = ErrnoLoc->getAsRegion();226 227  // If 'errno' is invalidated we can not know if it is checked or written into,228  // allow read and write without bug reports.229  if (llvm::is_contained(Regions, ErrnoRegion))230    return clearErrnoState(State);231 232  // Always reset errno state when the system memory space is invalidated.233  // The ErrnoRegion is not always found in the list in this case.234  if (llvm::is_contained(Regions, ErrnoRegion->getMemorySpace(State)))235    return clearErrnoState(State);236 237  return State;238}239 240void ento::registerErrnoChecker(CheckerManager &mgr) {241  const AnalyzerOptions &Opts = mgr.getAnalyzerOptions();242  auto *Checker = mgr.registerChecker<ErrnoChecker>();243  Checker->AllowErrnoReadOutsideConditions = Opts.getCheckerBooleanOption(244      Checker, "AllowErrnoReadOutsideConditionExpressions");245}246 247bool ento::shouldRegisterErrnoChecker(const CheckerManager &mgr) {248  return true;249}250