Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
35294 views
//=== ErrnoModeling.cpp -----------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This defines a checker `ErrnoModeling`, which is used to make the system9// value 'errno' available to other checkers.10// The 'errno' value is stored at a special memory region that is accessible11// through the `errno_modeling` namespace. The memory region is either the12// region of `errno` itself if it is a variable, otherwise an artifically13// created region (in the system memory space). If `errno` is defined by using14// a function which returns the address of it (this is always the case if it is15// not a variable) this function is recognized and evaluated. In this way16// `errno` becomes visible to the analysis and checkers can change its value.17//18//===----------------------------------------------------------------------===//1920#include "ErrnoModeling.h"21#include "clang/AST/ParentMapContext.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 "llvm/Support/FormatVariadic.h"31#include <optional>3233using namespace clang;34using namespace ento;3536namespace {3738// Name of the "errno" variable.39// FIXME: Is there a system where it is not called "errno" but is a variable?40const char *ErrnoVarName = "errno";4142// Names of functions that return a location of the "errno" value.43// FIXME: Are there other similar function names?44CallDescriptionSet ErrnoLocationCalls{45{CDM::CLibrary, {"__errno_location"}, 0, 0},46{CDM::CLibrary, {"___errno"}, 0, 0},47{CDM::CLibrary, {"__errno"}, 0, 0},48{CDM::CLibrary, {"_errno"}, 0, 0},49{CDM::CLibrary, {"__error"}, 0, 0}};5051class ErrnoModeling52: public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,53check::LiveSymbols, eval::Call> {54public:55void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,56BugReporter &BR) const;57void checkBeginFunction(CheckerContext &C) const;58void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;59bool evalCall(const CallEvent &Call, CheckerContext &C) const;6061private:62// The declaration of an "errno" variable on systems where errno is63// represented by a variable (and not a function that queries its location).64mutable const VarDecl *ErrnoDecl = nullptr;65};6667} // namespace6869/// Store a MemRegion that contains the 'errno' integer value.70/// The value is null if the 'errno' value was not recognized in the AST.71REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)7273REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)7475void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D,76AnalysisManager &Mgr, BugReporter &BR) const {77// Try to find the declaration of the external variable `int errno;`.78// There are also C library implementations, where the `errno` location is79// accessed via a function that returns its address; in those environments80// this callback has no effect.81ASTContext &ACtx = Mgr.getASTContext();82IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);83auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);84auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {85if (auto *VD = dyn_cast<VarDecl>(D))86return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&87VD->hasExternalStorage() &&88VD->getType().getCanonicalType() == ACtx.IntTy;89return false;90});91if (Found != LookupRes.end())92ErrnoDecl = cast<VarDecl>(*Found);93}9495void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {96if (!C.inTopFrame())97return;9899ASTContext &ACtx = C.getASTContext();100ProgramStateRef State = C.getState();101102const MemRegion *ErrnoR = nullptr;103104if (ErrnoDecl) {105// There is an external 'errno' variable, so we can simply use the memory106// region that's associated with it.107ErrnoR = State->getRegion(ErrnoDecl, C.getLocationContext());108assert(ErrnoR && "Memory region should exist for the 'errno' variable.");109} else {110// There is no 'errno' variable, so create a new symbolic memory region111// that can be used to model the return value of the "get the location of112// errno" internal functions.113// NOTE: this `SVal` is created even if errno is not defined or used.114SValBuilder &SVB = C.getSValBuilder();115MemRegionManager &RMgr = C.getStateManager().getRegionManager();116117const MemSpaceRegion *GlobalSystemSpace =118RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);119120// Create an artifical symbol for the region.121// Note that it is not possible to associate a statement or expression in122// this case and the `symbolTag` (opaque pointer tag) is just the address123// of the data member `ErrnoDecl` of the singleton `ErrnoModeling` checker124// object.125const SymbolConjured *Sym = SVB.conjureSymbol(126nullptr, C.getLocationContext(),127ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);128129// The symbolic region is untyped, create a typed sub-region in it.130// The ElementRegion is used to make the errno region a typed region.131ErrnoR = RMgr.getElementRegion(132ACtx.IntTy, SVB.makeZeroArrayIndex(),133RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());134}135assert(ErrnoR);136State = State->set<ErrnoRegion>(ErrnoR);137State =138errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);139C.addTransition(State);140}141142bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {143// Return location of "errno" at a call to an "errno address returning"144// function.145if (errno_modeling::isErrnoLocationCall(Call)) {146ProgramStateRef State = C.getState();147148const MemRegion *ErrnoR = State->get<ErrnoRegion>();149if (!ErrnoR)150return false;151152State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),153loc::MemRegionVal{ErrnoR});154C.addTransition(State);155return true;156}157158return false;159}160161void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,162SymbolReaper &SR) const {163// The special errno region should never garbage collected.164if (const auto *ErrnoR = State->get<ErrnoRegion>())165SR.markLive(ErrnoR);166}167168namespace clang {169namespace ento {170namespace errno_modeling {171172std::optional<SVal> getErrnoValue(ProgramStateRef State) {173const MemRegion *ErrnoR = State->get<ErrnoRegion>();174if (!ErrnoR)175return {};176QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;177return State->getSVal(ErrnoR, IntTy);178}179180ProgramStateRef setErrnoValue(ProgramStateRef State,181const LocationContext *LCtx, SVal Value,182ErrnoCheckState EState) {183const MemRegion *ErrnoR = State->get<ErrnoRegion>();184if (!ErrnoR)185return State;186// First set the errno value, the old state is still available at 'checkBind'187// or 'checkLocation' for errno value.188State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);189return State->set<ErrnoState>(EState);190}191192ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,193uint64_t Value, ErrnoCheckState EState) {194const MemRegion *ErrnoR = State->get<ErrnoRegion>();195if (!ErrnoR)196return State;197State = State->bindLoc(198loc::MemRegionVal{ErrnoR},199C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),200C.getLocationContext());201return State->set<ErrnoState>(EState);202}203204std::optional<Loc> getErrnoLoc(ProgramStateRef State) {205const MemRegion *ErrnoR = State->get<ErrnoRegion>();206if (!ErrnoR)207return {};208return loc::MemRegionVal{ErrnoR};209}210211ErrnoCheckState getErrnoState(ProgramStateRef State) {212return State->get<ErrnoState>();213}214215ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {216return State->set<ErrnoState>(EState);217}218219ProgramStateRef clearErrnoState(ProgramStateRef State) {220return setErrnoState(State, Irrelevant);221}222223bool isErrnoLocationCall(const CallEvent &CE) {224return ErrnoLocationCalls.contains(CE);225}226227const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {228return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {229const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();230if (ErrnoR && BR.isInteresting(ErrnoR)) {231BR.markNotInteresting(ErrnoR);232return Message;233}234return "";235});236}237238ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State,239CheckerContext &C) {240return setErrnoState(State, MustNotBeChecked);241}242243ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C,244NonLoc ErrnoSym) {245SValBuilder &SVB = C.getSValBuilder();246NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>();247DefinedOrUnknownSVal Cond =248SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType())249.castAs<DefinedOrUnknownSVal>();250State = State->assume(Cond, true);251if (!State)252return nullptr;253return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant);254}255256ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,257CheckerContext &C,258const Expr *InvalE) {259const MemRegion *ErrnoR = State->get<ErrnoRegion>();260if (!ErrnoR)261return State;262State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(),263C.getLocationContext(), false);264if (!State)265return nullptr;266return setErrnoState(State, MustBeChecked);267}268269} // namespace errno_modeling270} // namespace ento271} // namespace clang272273void ento::registerErrnoModeling(CheckerManager &mgr) {274mgr.registerChecker<ErrnoModeling>();275}276277bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {278return true;279}280281282