181ad6265SDimitry Andric //=== ErrnoModeling.cpp -----------------------------------------*- C++ -*-===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric // 981ad6265SDimitry Andric // This defines a checker `ErrnoModeling`, which is used to make the system 1081ad6265SDimitry Andric // value 'errno' available to other checkers. 1181ad6265SDimitry Andric // The 'errno' value is stored at a special memory region that is accessible 1281ad6265SDimitry Andric // through the `errno_modeling` namespace. The memory region is either the 1381ad6265SDimitry Andric // region of `errno` itself if it is a variable, otherwise an artifically 1481ad6265SDimitry Andric // created region (in the system memory space). If `errno` is defined by using 1581ad6265SDimitry Andric // a function which returns the address of it (this is always the case if it is 1681ad6265SDimitry Andric // not a variable) this function is recognized and evaluated. In this way 1781ad6265SDimitry Andric // `errno` becomes visible to the analysis and checkers can change its value. 1881ad6265SDimitry Andric // 1981ad6265SDimitry Andric //===----------------------------------------------------------------------===// 2081ad6265SDimitry Andric 2181ad6265SDimitry Andric #include "ErrnoModeling.h" 2281ad6265SDimitry Andric #include "clang/AST/ParentMapContext.h" 2381ad6265SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 2481ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 2581ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 2681ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" 2781ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 2881ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 2981ad6265SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" 3081ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h" 31*06c3fb27SDimitry Andric #include "llvm/Support/FormatVariadic.h" 32bdd1243dSDimitry Andric #include <optional> 3381ad6265SDimitry Andric 3481ad6265SDimitry Andric using namespace clang; 3581ad6265SDimitry Andric using namespace ento; 3681ad6265SDimitry Andric 3781ad6265SDimitry Andric namespace { 3881ad6265SDimitry Andric 3981ad6265SDimitry Andric // Name of the "errno" variable. 4081ad6265SDimitry Andric // FIXME: Is there a system where it is not called "errno" but is a variable? 4181ad6265SDimitry Andric const char *ErrnoVarName = "errno"; 4281ad6265SDimitry Andric // Names of functions that return a location of the "errno" value. 4381ad6265SDimitry Andric // FIXME: Are there other similar function names? 4481ad6265SDimitry Andric const char *ErrnoLocationFuncNames[] = {"__errno_location", "___errno", 4581ad6265SDimitry Andric "__errno", "_errno", "__error"}; 4681ad6265SDimitry Andric 4781ad6265SDimitry Andric class ErrnoModeling 4881ad6265SDimitry Andric : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction, 4981ad6265SDimitry Andric check::LiveSymbols, eval::Call> { 5081ad6265SDimitry Andric public: 5181ad6265SDimitry Andric void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr, 5281ad6265SDimitry Andric BugReporter &BR) const; 5381ad6265SDimitry Andric void checkBeginFunction(CheckerContext &C) const; 5481ad6265SDimitry Andric void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; 5581ad6265SDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const; 5681ad6265SDimitry Andric 5781ad6265SDimitry Andric // The declaration of an "errno" variable or "errno location" function. 5881ad6265SDimitry Andric mutable const Decl *ErrnoDecl = nullptr; 5981ad6265SDimitry Andric 6081ad6265SDimitry Andric private: 6181ad6265SDimitry Andric // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set. 62bdd1243dSDimitry Andric CallDescriptionSet ErrnoLocationCalls{{{"__errno_location"}, 0, 0}, 63bdd1243dSDimitry Andric {{"___errno"}, 0, 0}, 64bdd1243dSDimitry Andric {{"__errno"}, 0, 0}, 65bdd1243dSDimitry Andric {{"_errno"}, 0, 0}, 66bdd1243dSDimitry Andric {{"__error"}, 0, 0}}; 6781ad6265SDimitry Andric }; 6881ad6265SDimitry Andric 6981ad6265SDimitry Andric } // namespace 7081ad6265SDimitry Andric 7181ad6265SDimitry Andric /// Store a MemRegion that contains the 'errno' integer value. 7281ad6265SDimitry Andric /// The value is null if the 'errno' value was not recognized in the AST. 7381ad6265SDimitry Andric REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *) 7481ad6265SDimitry Andric 7581ad6265SDimitry Andric REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState) 7681ad6265SDimitry Andric 7781ad6265SDimitry Andric /// Search for a variable called "errno" in the AST. 7881ad6265SDimitry Andric /// Return nullptr if not found. 7981ad6265SDimitry Andric static const VarDecl *getErrnoVar(ASTContext &ACtx) { 8081ad6265SDimitry Andric IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName); 8181ad6265SDimitry Andric auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); 8281ad6265SDimitry Andric auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) { 8381ad6265SDimitry Andric if (auto *VD = dyn_cast<VarDecl>(D)) 8481ad6265SDimitry Andric return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) && 8581ad6265SDimitry Andric VD->hasExternalStorage() && 8681ad6265SDimitry Andric VD->getType().getCanonicalType() == ACtx.IntTy; 8781ad6265SDimitry Andric return false; 8881ad6265SDimitry Andric }); 8981ad6265SDimitry Andric if (Found == LookupRes.end()) 9081ad6265SDimitry Andric return nullptr; 9181ad6265SDimitry Andric 9281ad6265SDimitry Andric return cast<VarDecl>(*Found); 9381ad6265SDimitry Andric } 9481ad6265SDimitry Andric 9581ad6265SDimitry Andric /// Search for a function with a specific name that is used to return a pointer 9681ad6265SDimitry Andric /// to "errno". 9781ad6265SDimitry Andric /// Return nullptr if no such function was found. 9881ad6265SDimitry Andric static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) { 9981ad6265SDimitry Andric SmallVector<const Decl *> LookupRes; 10081ad6265SDimitry Andric for (StringRef ErrnoName : ErrnoLocationFuncNames) { 10181ad6265SDimitry Andric IdentifierInfo &II = ACtx.Idents.get(ErrnoName); 10281ad6265SDimitry Andric llvm::append_range(LookupRes, ACtx.getTranslationUnitDecl()->lookup(&II)); 10381ad6265SDimitry Andric } 10481ad6265SDimitry Andric 10581ad6265SDimitry Andric auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) { 10681ad6265SDimitry Andric if (auto *FD = dyn_cast<FunctionDecl>(D)) 10781ad6265SDimitry Andric return ACtx.getSourceManager().isInSystemHeader(FD->getLocation()) && 10881ad6265SDimitry Andric FD->isExternC() && FD->getNumParams() == 0 && 10981ad6265SDimitry Andric FD->getReturnType().getCanonicalType() == 11081ad6265SDimitry Andric ACtx.getPointerType(ACtx.IntTy); 11181ad6265SDimitry Andric return false; 11281ad6265SDimitry Andric }); 11381ad6265SDimitry Andric if (Found == LookupRes.end()) 11481ad6265SDimitry Andric return nullptr; 11581ad6265SDimitry Andric 11681ad6265SDimitry Andric return cast<FunctionDecl>(*Found); 11781ad6265SDimitry Andric } 11881ad6265SDimitry Andric 11981ad6265SDimitry Andric void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D, 12081ad6265SDimitry Andric AnalysisManager &Mgr, BugReporter &BR) const { 12181ad6265SDimitry Andric // Try to find an usable `errno` value. 12281ad6265SDimitry Andric // It can be an external variable called "errno" or a function that returns a 12381ad6265SDimitry Andric // pointer to the "errno" value. This function can have different names. 12481ad6265SDimitry Andric // The actual case is dependent on the C library implementation, we 12581ad6265SDimitry Andric // can only search for a match in one of these variations. 12681ad6265SDimitry Andric // We assume that exactly one of these cases might be true. 12781ad6265SDimitry Andric ErrnoDecl = getErrnoVar(Mgr.getASTContext()); 12881ad6265SDimitry Andric if (!ErrnoDecl) 12981ad6265SDimitry Andric ErrnoDecl = getErrnoFunc(Mgr.getASTContext()); 13081ad6265SDimitry Andric } 13181ad6265SDimitry Andric 13281ad6265SDimitry Andric void ErrnoModeling::checkBeginFunction(CheckerContext &C) const { 13381ad6265SDimitry Andric if (!C.inTopFrame()) 13481ad6265SDimitry Andric return; 13581ad6265SDimitry Andric 13681ad6265SDimitry Andric ASTContext &ACtx = C.getASTContext(); 13781ad6265SDimitry Andric ProgramStateRef State = C.getState(); 13881ad6265SDimitry Andric 13981ad6265SDimitry Andric if (const auto *ErrnoVar = dyn_cast_or_null<VarDecl>(ErrnoDecl)) { 14081ad6265SDimitry Andric // There is an external 'errno' variable. 14181ad6265SDimitry Andric // Use its memory region. 14281ad6265SDimitry Andric // The memory region for an 'errno'-like variable is allocated in system 14381ad6265SDimitry Andric // space by MemRegionManager. 14481ad6265SDimitry Andric const MemRegion *ErrnoR = 14581ad6265SDimitry Andric State->getRegion(ErrnoVar, C.getLocationContext()); 14681ad6265SDimitry Andric assert(ErrnoR && "Memory region should exist for the 'errno' variable."); 14781ad6265SDimitry Andric State = State->set<ErrnoRegion>(ErrnoR); 14881ad6265SDimitry Andric State = 14981ad6265SDimitry Andric errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant); 15081ad6265SDimitry Andric C.addTransition(State); 15181ad6265SDimitry Andric } else if (ErrnoDecl) { 15281ad6265SDimitry Andric assert(isa<FunctionDecl>(ErrnoDecl) && "Invalid errno location function."); 15381ad6265SDimitry Andric // There is a function that returns the location of 'errno'. 15481ad6265SDimitry Andric // We must create a memory region for it in system space. 15581ad6265SDimitry Andric // Currently a symbolic region is used with an artifical symbol. 15681ad6265SDimitry Andric // FIXME: It is better to have a custom (new) kind of MemRegion for such 15781ad6265SDimitry Andric // cases. 15881ad6265SDimitry Andric SValBuilder &SVB = C.getSValBuilder(); 15981ad6265SDimitry Andric MemRegionManager &RMgr = C.getStateManager().getRegionManager(); 16081ad6265SDimitry Andric 16181ad6265SDimitry Andric const MemSpaceRegion *GlobalSystemSpace = 16281ad6265SDimitry Andric RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); 16381ad6265SDimitry Andric 16481ad6265SDimitry Andric // Create an artifical symbol for the region. 16581ad6265SDimitry Andric // It is not possible to associate a statement or expression in this case. 16681ad6265SDimitry Andric const SymbolConjured *Sym = SVB.conjureSymbol( 16781ad6265SDimitry Andric nullptr, C.getLocationContext(), 16881ad6265SDimitry Andric ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl); 16981ad6265SDimitry Andric 17081ad6265SDimitry Andric // The symbolic region is untyped, create a typed sub-region in it. 17181ad6265SDimitry Andric // The ElementRegion is used to make the errno region a typed region. 17281ad6265SDimitry Andric const MemRegion *ErrnoR = RMgr.getElementRegion( 17381ad6265SDimitry Andric ACtx.IntTy, SVB.makeZeroArrayIndex(), 17481ad6265SDimitry Andric RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext()); 17581ad6265SDimitry Andric State = State->set<ErrnoRegion>(ErrnoR); 17681ad6265SDimitry Andric State = 17781ad6265SDimitry Andric errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant); 17881ad6265SDimitry Andric C.addTransition(State); 17981ad6265SDimitry Andric } 18081ad6265SDimitry Andric } 18181ad6265SDimitry Andric 18281ad6265SDimitry Andric bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { 18381ad6265SDimitry Andric // Return location of "errno" at a call to an "errno address returning" 18481ad6265SDimitry Andric // function. 18581ad6265SDimitry Andric if (ErrnoLocationCalls.contains(Call)) { 18681ad6265SDimitry Andric ProgramStateRef State = C.getState(); 18781ad6265SDimitry Andric 18881ad6265SDimitry Andric const MemRegion *ErrnoR = State->get<ErrnoRegion>(); 18981ad6265SDimitry Andric if (!ErrnoR) 19081ad6265SDimitry Andric return false; 19181ad6265SDimitry Andric 19281ad6265SDimitry Andric State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), 19381ad6265SDimitry Andric loc::MemRegionVal{ErrnoR}); 19481ad6265SDimitry Andric C.addTransition(State); 19581ad6265SDimitry Andric return true; 19681ad6265SDimitry Andric } 19781ad6265SDimitry Andric 19881ad6265SDimitry Andric return false; 19981ad6265SDimitry Andric } 20081ad6265SDimitry Andric 20181ad6265SDimitry Andric void ErrnoModeling::checkLiveSymbols(ProgramStateRef State, 20281ad6265SDimitry Andric SymbolReaper &SR) const { 20381ad6265SDimitry Andric // The special errno region should never garbage collected. 20481ad6265SDimitry Andric if (const auto *ErrnoR = State->get<ErrnoRegion>()) 20581ad6265SDimitry Andric SR.markLive(ErrnoR); 20681ad6265SDimitry Andric } 20781ad6265SDimitry Andric 20881ad6265SDimitry Andric namespace clang { 20981ad6265SDimitry Andric namespace ento { 21081ad6265SDimitry Andric namespace errno_modeling { 21181ad6265SDimitry Andric 212bdd1243dSDimitry Andric std::optional<SVal> getErrnoValue(ProgramStateRef State) { 21381ad6265SDimitry Andric const MemRegion *ErrnoR = State->get<ErrnoRegion>(); 21481ad6265SDimitry Andric if (!ErrnoR) 21581ad6265SDimitry Andric return {}; 21681ad6265SDimitry Andric QualType IntTy = State->getAnalysisManager().getASTContext().IntTy; 21781ad6265SDimitry Andric return State->getSVal(ErrnoR, IntTy); 21881ad6265SDimitry Andric } 21981ad6265SDimitry Andric 22081ad6265SDimitry Andric ProgramStateRef setErrnoValue(ProgramStateRef State, 22181ad6265SDimitry Andric const LocationContext *LCtx, SVal Value, 22281ad6265SDimitry Andric ErrnoCheckState EState) { 22381ad6265SDimitry Andric const MemRegion *ErrnoR = State->get<ErrnoRegion>(); 22481ad6265SDimitry Andric if (!ErrnoR) 22581ad6265SDimitry Andric return State; 22681ad6265SDimitry Andric // First set the errno value, the old state is still available at 'checkBind' 22781ad6265SDimitry Andric // or 'checkLocation' for errno value. 22881ad6265SDimitry Andric State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx); 22981ad6265SDimitry Andric return State->set<ErrnoState>(EState); 23081ad6265SDimitry Andric } 23181ad6265SDimitry Andric 23281ad6265SDimitry Andric ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, 23381ad6265SDimitry Andric uint64_t Value, ErrnoCheckState EState) { 23481ad6265SDimitry Andric const MemRegion *ErrnoR = State->get<ErrnoRegion>(); 23581ad6265SDimitry Andric if (!ErrnoR) 23681ad6265SDimitry Andric return State; 23781ad6265SDimitry Andric State = State->bindLoc( 23881ad6265SDimitry Andric loc::MemRegionVal{ErrnoR}, 23981ad6265SDimitry Andric C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy), 24081ad6265SDimitry Andric C.getLocationContext()); 24181ad6265SDimitry Andric return State->set<ErrnoState>(EState); 24281ad6265SDimitry Andric } 24381ad6265SDimitry Andric 244bdd1243dSDimitry Andric std::optional<Loc> getErrnoLoc(ProgramStateRef State) { 24581ad6265SDimitry Andric const MemRegion *ErrnoR = State->get<ErrnoRegion>(); 24681ad6265SDimitry Andric if (!ErrnoR) 24781ad6265SDimitry Andric return {}; 24881ad6265SDimitry Andric return loc::MemRegionVal{ErrnoR}; 24981ad6265SDimitry Andric } 25081ad6265SDimitry Andric 251bdd1243dSDimitry Andric ErrnoCheckState getErrnoState(ProgramStateRef State) { 252bdd1243dSDimitry Andric return State->get<ErrnoState>(); 253bdd1243dSDimitry Andric } 254bdd1243dSDimitry Andric 25581ad6265SDimitry Andric ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) { 25681ad6265SDimitry Andric return State->set<ErrnoState>(EState); 25781ad6265SDimitry Andric } 25881ad6265SDimitry Andric 259bdd1243dSDimitry Andric ProgramStateRef clearErrnoState(ProgramStateRef State) { 260bdd1243dSDimitry Andric return setErrnoState(State, Irrelevant); 26181ad6265SDimitry Andric } 26281ad6265SDimitry Andric 26381ad6265SDimitry Andric bool isErrno(const Decl *D) { 26481ad6265SDimitry Andric if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) 26581ad6265SDimitry Andric if (const IdentifierInfo *II = VD->getIdentifier()) 26681ad6265SDimitry Andric return II->getName() == ErrnoVarName; 26781ad6265SDimitry Andric if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) 26881ad6265SDimitry Andric if (const IdentifierInfo *II = FD->getIdentifier()) 26981ad6265SDimitry Andric return llvm::is_contained(ErrnoLocationFuncNames, II->getName()); 27081ad6265SDimitry Andric return false; 27181ad6265SDimitry Andric } 27281ad6265SDimitry Andric 27381ad6265SDimitry Andric const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) { 27481ad6265SDimitry Andric return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string { 27581ad6265SDimitry Andric const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>(); 27681ad6265SDimitry Andric if (ErrnoR && BR.isInteresting(ErrnoR)) { 27781ad6265SDimitry Andric BR.markNotInteresting(ErrnoR); 27881ad6265SDimitry Andric return Message; 27981ad6265SDimitry Andric } 28081ad6265SDimitry Andric return ""; 28181ad6265SDimitry Andric }); 28281ad6265SDimitry Andric } 28381ad6265SDimitry Andric 284bdd1243dSDimitry Andric ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, 285bdd1243dSDimitry Andric CheckerContext &C) { 286bdd1243dSDimitry Andric return setErrnoState(State, MustNotBeChecked); 287bdd1243dSDimitry Andric } 288bdd1243dSDimitry Andric 289bdd1243dSDimitry Andric ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, 290bdd1243dSDimitry Andric NonLoc ErrnoSym) { 291bdd1243dSDimitry Andric SValBuilder &SVB = C.getSValBuilder(); 292bdd1243dSDimitry Andric NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>(); 293bdd1243dSDimitry Andric DefinedOrUnknownSVal Cond = 294bdd1243dSDimitry Andric SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType()) 295bdd1243dSDimitry Andric .castAs<DefinedOrUnknownSVal>(); 296bdd1243dSDimitry Andric State = State->assume(Cond, true); 297bdd1243dSDimitry Andric if (!State) 298bdd1243dSDimitry Andric return nullptr; 299bdd1243dSDimitry Andric return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant); 300bdd1243dSDimitry Andric } 301bdd1243dSDimitry Andric 302bdd1243dSDimitry Andric ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, 303bdd1243dSDimitry Andric CheckerContext &C, 304bdd1243dSDimitry Andric const Expr *InvalE) { 305bdd1243dSDimitry Andric const MemRegion *ErrnoR = State->get<ErrnoRegion>(); 306bdd1243dSDimitry Andric if (!ErrnoR) 307bdd1243dSDimitry Andric return State; 308bdd1243dSDimitry Andric State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(), 309bdd1243dSDimitry Andric C.getLocationContext(), false); 310bdd1243dSDimitry Andric if (!State) 311bdd1243dSDimitry Andric return nullptr; 312bdd1243dSDimitry Andric return setErrnoState(State, MustBeChecked); 313bdd1243dSDimitry Andric } 314bdd1243dSDimitry Andric 315bdd1243dSDimitry Andric const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn) { 316bdd1243dSDimitry Andric return getErrnoNoteTag( 317*06c3fb27SDimitry Andric C, llvm::formatv( 318*06c3fb27SDimitry Andric "'errno' may be undefined after successful call to '{0}'", Fn)); 319bdd1243dSDimitry Andric } 320bdd1243dSDimitry Andric 321bdd1243dSDimitry Andric const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C, 322bdd1243dSDimitry Andric llvm::StringRef Fn) { 323bdd1243dSDimitry Andric return getErrnoNoteTag( 324*06c3fb27SDimitry Andric C, llvm::formatv("'{0}' indicates failure only by setting 'errno'", Fn)); 325bdd1243dSDimitry Andric } 326bdd1243dSDimitry Andric 32781ad6265SDimitry Andric } // namespace errno_modeling 32881ad6265SDimitry Andric } // namespace ento 32981ad6265SDimitry Andric } // namespace clang 33081ad6265SDimitry Andric 33181ad6265SDimitry Andric void ento::registerErrnoModeling(CheckerManager &mgr) { 33281ad6265SDimitry Andric mgr.registerChecker<ErrnoModeling>(); 33381ad6265SDimitry Andric } 33481ad6265SDimitry Andric 33581ad6265SDimitry Andric bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) { 33681ad6265SDimitry Andric return true; 33781ad6265SDimitry Andric } 338