@@ -39,10 +39,15 @@ namespace {
3939// Name of the "errno" variable.
4040// FIXME: Is there a system where it is not called "errno" but is a variable?
4141const char *ErrnoVarName = " errno" ;
42+
4243// Names of functions that return a location of the "errno" value.
4344// FIXME: Are there other similar function names?
44- const char *ErrnoLocationFuncNames[] = {" __errno_location" , " ___errno" ,
45- " __errno" , " _errno" , " __error" };
45+ CallDescriptionSet ErrnoLocationCalls{
46+ {CDM::CLibrary, {" __errno_location" }, 0 , 0 },
47+ {CDM::CLibrary, {" ___errno" }, 0 , 0 },
48+ {CDM::CLibrary, {" __errno" }, 0 , 0 },
49+ {CDM::CLibrary, {" _errno" }, 0 , 0 },
50+ {CDM::CLibrary, {" __error" }, 0 , 0 }};
4651
4752class ErrnoModeling
4853 : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
@@ -54,16 +59,10 @@ class ErrnoModeling
5459 void checkLiveSymbols (ProgramStateRef State, SymbolReaper &SR) const ;
5560 bool evalCall (const CallEvent &Call, CheckerContext &C) const ;
5661
57- // The declaration of an "errno" variable or "errno location" function.
58- mutable const Decl *ErrnoDecl = nullptr ;
59-
6062private:
61- // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set.
62- CallDescriptionSet ErrnoLocationCalls{{{" __errno_location" }, 0 , 0 },
63- {{" ___errno" }, 0 , 0 },
64- {{" __errno" }, 0 , 0 },
65- {{" _errno" }, 0 , 0 },
66- {{" __error" }, 0 , 0 }};
63+ // The declaration of an "errno" variable on systems where errno is
64+ // represented by a variable (and not a function that queries its location).
65+ mutable const VarDecl *ErrnoDecl = nullptr ;
6766};
6867
6968} // namespace
@@ -74,9 +73,13 @@ REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)
7473
7574REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)
7675
77- // / Search for a variable called "errno" in the AST.
78- // / Return nullptr if not found.
79- static const VarDecl *getErrnoVar(ASTContext &ACtx) {
76+ void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D,
77+ AnalysisManager &Mgr, BugReporter &BR) const {
78+ // Try to find the declaration of the external variable `int errno;`.
79+ // There are also C library implementations, where the `errno` location is
80+ // accessed via a function that returns its address; in those environments
81+ // this callback has no effect.
82+ ASTContext &ACtx = Mgr.getASTContext ();
8083 IdentifierInfo &II = ACtx.Idents .get (ErrnoVarName);
8184 auto LookupRes = ACtx.getTranslationUnitDecl ()->lookup (&II);
8285 auto Found = llvm::find_if (LookupRes, [&ACtx](const Decl *D) {
@@ -86,47 +89,8 @@ static const VarDecl *getErrnoVar(ASTContext &ACtx) {
8689 VD->getType ().getCanonicalType () == ACtx.IntTy ;
8790 return false ;
8891 });
89- if (Found == LookupRes.end ())
90- return nullptr ;
91-
92- return cast<VarDecl>(*Found);
93- }
94-
95- // / Search for a function with a specific name that is used to return a pointer
96- // / to "errno".
97- // / Return nullptr if no such function was found.
98- static const FunctionDecl *getErrnoFunc (ASTContext &ACtx) {
99- SmallVector<const Decl *> LookupRes;
100- for (StringRef ErrnoName : ErrnoLocationFuncNames) {
101- IdentifierInfo &II = ACtx.Idents .get (ErrnoName);
102- llvm::append_range (LookupRes, ACtx.getTranslationUnitDecl ()->lookup (&II));
103- }
104-
105- auto Found = llvm::find_if (LookupRes, [&ACtx](const Decl *D) {
106- if (auto *FD = dyn_cast<FunctionDecl>(D))
107- return ACtx.getSourceManager ().isInSystemHeader (FD->getLocation ()) &&
108- FD->isExternC () && FD->getNumParams () == 0 &&
109- FD->getReturnType ().getCanonicalType () ==
110- ACtx.getPointerType (ACtx.IntTy );
111- return false ;
112- });
113- if (Found == LookupRes.end ())
114- return nullptr ;
115-
116- return cast<FunctionDecl>(*Found);
117- }
118-
119- void ErrnoModeling::checkASTDecl (const TranslationUnitDecl *D,
120- AnalysisManager &Mgr, BugReporter &BR) const {
121- // Try to find an usable `errno` value.
122- // It can be an external variable called "errno" or a function that returns a
123- // pointer to the "errno" value. This function can have different names.
124- // The actual case is dependent on the C library implementation, we
125- // can only search for a match in one of these variations.
126- // We assume that exactly one of these cases might be true.
127- ErrnoDecl = getErrnoVar (Mgr.getASTContext ());
128- if (!ErrnoDecl)
129- ErrnoDecl = getErrnoFunc (Mgr.getASTContext ());
92+ if (Found != LookupRes.end ())
93+ ErrnoDecl = cast<VarDecl>(*Found);
13094}
13195
13296void ErrnoModeling::checkBeginFunction (CheckerContext &C) const {
@@ -136,53 +100,50 @@ void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
136100 ASTContext &ACtx = C.getASTContext ();
137101 ProgramStateRef State = C.getState ();
138102
139- if (const auto *ErrnoVar = dyn_cast_or_null<VarDecl>(ErrnoDecl)) {
140- // There is an external 'errno' variable.
141- // Use its memory region.
142- // The memory region for an 'errno'-like variable is allocated in system
143- // space by MemRegionManager.
144- const MemRegion *ErrnoR =
145- State->getRegion (ErrnoVar, C.getLocationContext ());
103+ const MemRegion *ErrnoR = nullptr ;
104+
105+ if (ErrnoDecl) {
106+ // There is an external 'errno' variable, so we can simply use the memory
107+ // region that's associated with it.
108+ ErrnoR = State->getRegion (ErrnoDecl, C.getLocationContext ());
146109 assert (ErrnoR && " Memory region should exist for the 'errno' variable." );
147- State = State->set <ErrnoRegion>(ErrnoR);
148- State =
149- errno_modeling::setErrnoValue (State, C, 0 , errno_modeling::Irrelevant);
150- C.addTransition (State);
151- } else if (ErrnoDecl) {
152- assert (isa<FunctionDecl>(ErrnoDecl) && " Invalid errno location function." );
153- // There is a function that returns the location of 'errno'.
154- // We must create a memory region for it in system space.
155- // Currently a symbolic region is used with an artifical symbol.
156- // FIXME: It is better to have a custom (new) kind of MemRegion for such
157- // cases.
110+ } else {
111+ // There is no 'errno' variable, so create a new symbolic memory region
112+ // that can be used to model the return value of the "get the location of
113+ // errno" internal functions.
114+ // NOTE: this `SVal` is created even if errno is not defined or used.
158115 SValBuilder &SVB = C.getSValBuilder ();
159116 MemRegionManager &RMgr = C.getStateManager ().getRegionManager ();
160117
161118 const MemSpaceRegion *GlobalSystemSpace =
162119 RMgr.getGlobalsRegion (MemRegion::GlobalSystemSpaceRegionKind);
163120
164121 // Create an artifical symbol for the region.
165- // It is not possible to associate a statement or expression in this case.
122+ // Note that it is not possible to associate a statement or expression in
123+ // this case and the `symbolTag` (opaque pointer tag) is just the address
124+ // of the data member `ErrnoDecl` of the singleton `ErrnoModeling` checker
125+ // object.
166126 const SymbolConjured *Sym = SVB.conjureSymbol (
167127 nullptr , C.getLocationContext (),
168128 ACtx.getLValueReferenceType (ACtx.IntTy ), C.blockCount (), &ErrnoDecl);
169129
170130 // The symbolic region is untyped, create a typed sub-region in it.
171131 // The ElementRegion is used to make the errno region a typed region.
172- const MemRegion * ErrnoR = RMgr.getElementRegion (
132+ ErrnoR = RMgr.getElementRegion (
173133 ACtx.IntTy , SVB.makeZeroArrayIndex (),
174134 RMgr.getSymbolicRegion (Sym, GlobalSystemSpace), C.getASTContext ());
175- State = State->set <ErrnoRegion>(ErrnoR);
176- State =
177- errno_modeling::setErrnoValue (State, C, 0 , errno_modeling::Irrelevant);
178- C.addTransition (State);
179135 }
136+ assert (ErrnoR);
137+ State = State->set <ErrnoRegion>(ErrnoR);
138+ State =
139+ errno_modeling::setErrnoValue (State, C, 0 , errno_modeling::Irrelevant);
140+ C.addTransition (State);
180141}
181142
182143bool ErrnoModeling::evalCall (const CallEvent &Call, CheckerContext &C) const {
183144 // Return location of "errno" at a call to an "errno address returning"
184145 // function.
185- if (ErrnoLocationCalls. contains (Call)) {
146+ if (errno_modeling::isErrnoLocationCall (Call)) {
186147 ProgramStateRef State = C.getState ();
187148
188149 const MemRegion *ErrnoR = State->get <ErrnoRegion>();
@@ -260,14 +221,8 @@ ProgramStateRef clearErrnoState(ProgramStateRef State) {
260221 return setErrnoState (State, Irrelevant);
261222}
262223
263- bool isErrno (const Decl *D) {
264- if (const auto *VD = dyn_cast_or_null<VarDecl>(D))
265- if (const IdentifierInfo *II = VD->getIdentifier ())
266- return II->getName () == ErrnoVarName;
267- if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D))
268- if (const IdentifierInfo *II = FD->getIdentifier ())
269- return llvm::is_contained (ErrnoLocationFuncNames, II->getName ());
270- return false ;
224+ bool isErrnoLocationCall (const CallEvent &CE) {
225+ return ErrnoLocationCalls.contains (CE);
271226}
272227
273228const NoteTag *getErrnoNoteTag (CheckerContext &C, const std::string &Message) {
0 commit comments