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