brintos

brintos / llvm-project-archived public Read only

0
0
Text · 10.4 KiB · 46c7a6c Raw
279 lines · cpp
1//=== ErrnoModeling.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 a checker `ErrnoModeling`, which is used to make the system10// value 'errno' available to other checkers.11// The 'errno' value is stored at a special memory region that is accessible12// through the `errno_modeling` namespace. The memory region is either the13// region of `errno` itself if it is a variable, otherwise an artifically14// created region (in the system memory space). If `errno` is defined by using15// a function which returns the address of it (this is always the case if it is16// not a variable) this function is recognized and evaluated. In this way17// `errno` becomes visible to the analysis and checkers can change its value.18//19//===----------------------------------------------------------------------===//20 21#include "ErrnoModeling.h"22#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"23#include "clang/StaticAnalyzer/Core/Checker.h"24#include "clang/StaticAnalyzer/Core/CheckerManager.h"25#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"26#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"27#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"28#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"29#include "llvm/ADT/STLExtras.h"30#include <optional>31 32using namespace clang;33using namespace ento;34 35namespace {36 37// Name of the "errno" variable.38// FIXME: Is there a system where it is not called "errno" but is a variable?39const char *ErrnoVarName = "errno";40 41// Names of functions that return a location of the "errno" value.42// FIXME: Are there other similar function names?43CallDescriptionSet ErrnoLocationCalls{44    {CDM::CLibrary, {"__errno_location"}, 0, 0},45    {CDM::CLibrary, {"___errno"}, 0, 0},46    {CDM::CLibrary, {"__errno"}, 0, 0},47    {CDM::CLibrary, {"_errno"}, 0, 0},48    {CDM::CLibrary, {"__error"}, 0, 0}};49 50class ErrnoModeling51    : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,52                     check::LiveSymbols, eval::Call> {53public:54  void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,55                    BugReporter &BR) const;56  void checkBeginFunction(CheckerContext &C) const;57  void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;58  bool evalCall(const CallEvent &Call, CheckerContext &C) const;59 60private:61  // The declaration of an "errno" variable on systems where errno is62  // represented by a variable (and not a function that queries its location).63  mutable const VarDecl *ErrnoDecl = nullptr;64};65 66} // namespace67 68/// Store a MemRegion that contains the 'errno' integer value.69/// The value is null if the 'errno' value was not recognized in the AST.70REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)71 72REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)73 74void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D,75                                 AnalysisManager &Mgr, BugReporter &BR) const {76  // Try to find the declaration of the external variable `int errno;`.77  // There are also C library implementations, where the `errno` location is78  // accessed via a function that returns its address; in those environments79  // this callback has no effect.80  ASTContext &ACtx = Mgr.getASTContext();81  IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);82  auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);83  auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {84    if (auto *VD = dyn_cast<VarDecl>(D))85      return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&86             VD->hasExternalStorage() &&87             VD->getType().getCanonicalType() == ACtx.IntTy;88    return false;89  });90  if (Found != LookupRes.end())91    ErrnoDecl = cast<VarDecl>(*Found);92}93 94void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {95  if (!C.inTopFrame())96    return;97 98  ASTContext &ACtx = C.getASTContext();99  ProgramStateRef State = C.getState();100 101  const MemRegion *ErrnoR = nullptr;102 103  if (ErrnoDecl) {104    // There is an external 'errno' variable, so we can simply use the memory105    // region that's associated with it.106    ErrnoR = State->getRegion(ErrnoDecl, C.getLocationContext());107    assert(ErrnoR && "Memory region should exist for the 'errno' variable.");108  } else {109    // There is no 'errno' variable, so create a new symbolic memory region110    // that can be used to model the return value of the "get the location of111    // errno" internal functions.112    // NOTE: this `SVal` is created even if errno is not defined or used.113    SValBuilder &SVB = C.getSValBuilder();114    MemRegionManager &RMgr = C.getStateManager().getRegionManager();115 116    const MemSpaceRegion *GlobalSystemSpace =117        RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);118 119    // Create an artifical symbol for the region.120    // Note that it is not possible to associate a statement or expression in121    // this case and the `symbolTag` (opaque pointer tag) is just the address122    // of the data member `ErrnoDecl` of the singleton `ErrnoModeling` checker123    // object.124    const SymbolConjured *Sym = SVB.conjureSymbol(125        C.getCFGElementRef(), C.getLocationContext(),126        ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);127 128    // The symbolic region is untyped, create a typed sub-region in it.129    // The ElementRegion is used to make the errno region a typed region.130    ErrnoR = RMgr.getElementRegion(131        ACtx.IntTy, SVB.makeZeroArrayIndex(),132        RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());133  }134  assert(ErrnoR);135  State = State->set<ErrnoRegion>(ErrnoR);136  State =137      errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);138  C.addTransition(State);139}140 141bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {142  // Return location of "errno" at a call to an "errno address returning"143  // function.144  if (errno_modeling::isErrnoLocationCall(Call)) {145    ProgramStateRef State = C.getState();146 147    const MemRegion *ErrnoR = State->get<ErrnoRegion>();148    if (!ErrnoR)149      return false;150 151    State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),152                            loc::MemRegionVal{ErrnoR});153    C.addTransition(State);154    return true;155  }156 157  return false;158}159 160void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,161                                     SymbolReaper &SR) const {162  // The special errno region should never garbage collected.163  if (const auto *ErrnoR = State->get<ErrnoRegion>())164    SR.markLive(ErrnoR);165}166 167namespace clang {168namespace ento {169namespace errno_modeling {170 171std::optional<SVal> getErrnoValue(ProgramStateRef State) {172  const MemRegion *ErrnoR = State->get<ErrnoRegion>();173  if (!ErrnoR)174    return {};175  QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;176  return State->getSVal(ErrnoR, IntTy);177}178 179ProgramStateRef setErrnoValue(ProgramStateRef State,180                              const LocationContext *LCtx, SVal Value,181                              ErrnoCheckState EState) {182  const MemRegion *ErrnoR = State->get<ErrnoRegion>();183  if (!ErrnoR)184    return State;185  // First set the errno value, the old state is still available at 'checkBind'186  // or 'checkLocation' for errno value.187  State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);188  return State->set<ErrnoState>(EState);189}190 191ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,192                              uint64_t Value, ErrnoCheckState EState) {193  const MemRegion *ErrnoR = State->get<ErrnoRegion>();194  if (!ErrnoR)195    return State;196  State = State->bindLoc(197      loc::MemRegionVal{ErrnoR},198      C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),199      C.getLocationContext());200  return State->set<ErrnoState>(EState);201}202 203std::optional<Loc> getErrnoLoc(ProgramStateRef State) {204  const MemRegion *ErrnoR = State->get<ErrnoRegion>();205  if (!ErrnoR)206    return {};207  return loc::MemRegionVal{ErrnoR};208}209 210ErrnoCheckState getErrnoState(ProgramStateRef State) {211  return State->get<ErrnoState>();212}213 214ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {215  return State->set<ErrnoState>(EState);216}217 218ProgramStateRef clearErrnoState(ProgramStateRef State) {219  return setErrnoState(State, Irrelevant);220}221 222bool isErrnoLocationCall(const CallEvent &CE) {223  return ErrnoLocationCalls.contains(CE);224}225 226const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {227  return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {228    const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();229    if (ErrnoR && BR.isInteresting(ErrnoR)) {230      BR.markNotInteresting(ErrnoR);231      return Message;232    }233    return "";234  });235}236 237ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State,238                                      CheckerContext &C) {239  return setErrnoState(State, MustNotBeChecked);240}241 242ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C,243                                      NonLoc ErrnoSym) {244  SValBuilder &SVB = C.getSValBuilder();245  NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>();246  DefinedOrUnknownSVal Cond =247      SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType())248          .castAs<DefinedOrUnknownSVal>();249  State = State->assume(Cond, true);250  if (!State)251    return nullptr;252  return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant);253}254 255ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,256                                         CheckerContext &C,257                                         ConstCFGElementRef Elem) {258  const MemRegion *ErrnoR = State->get<ErrnoRegion>();259  if (!ErrnoR)260    return State;261  State = State->invalidateRegions(ErrnoR, Elem, C.blockCount(),262                                   C.getLocationContext(), false);263  if (!State)264    return nullptr;265  return setErrnoState(State, MustBeChecked);266}267 268} // namespace errno_modeling269} // namespace ento270} // namespace clang271 272void ento::registerErrnoModeling(CheckerManager &mgr) {273  mgr.registerChecker<ErrnoModeling>();274}275 276bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {277  return true;278}279