clang 19.0.0git
ErrnoModeling.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This defines a checker `ErrnoModeling`, which is used to make the system
10// value 'errno' available to other checkers.
11// The 'errno' value is stored at a special memory region that is accessible
12// through the `errno_modeling` namespace. The memory region is either the
13// region of `errno` itself if it is a variable, otherwise an artifically
14// created region (in the system memory space). If `errno` is defined by using
15// a function which returns the address of it (this is always the case if it is
16// not a variable) this function is recognized and evaluated. In this way
17// `errno` becomes visible to the analysis and checkers can change its value.
18//
19//===----------------------------------------------------------------------===//
20
21#include "ErrnoModeling.h"
30#include "llvm/ADT/STLExtras.h"
31#include "llvm/Support/FormatVariadic.h"
32#include <optional>
33
34using namespace clang;
35using namespace ento;
36
37namespace {
38
39// Name of the "errno" variable.
40// FIXME: Is there a system where it is not called "errno" but is a variable?
41const char *ErrnoVarName = "errno";
42// Names of functions that return a location of the "errno" value.
43// FIXME: Are there other similar function names?
44const char *ErrnoLocationFuncNames[] = {"__errno_location", "___errno",
45 "__errno", "_errno", "__error"};
46
47class ErrnoModeling
48 : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
49 check::LiveSymbols, eval::Call> {
50public:
51 void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,
52 BugReporter &BR) const;
53 void checkBeginFunction(CheckerContext &C) const;
54 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
55 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
56
57 // The declaration of an "errno" variable or "errno location" function.
58 mutable const Decl *ErrnoDecl = nullptr;
59
60private:
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}};
67};
68
69} // namespace
70
71/// Store a MemRegion that contains the 'errno' integer value.
72/// The value is null if the 'errno' value was not recognized in the AST.
74
76
77/// Search for a variable called "errno" in the AST.
78/// Return nullptr if not found.
79static const VarDecl *getErrnoVar(ASTContext &ACtx) {
80 IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);
81 auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
82 auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
83 if (auto *VD = dyn_cast<VarDecl>(D))
84 return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&
85 VD->hasExternalStorage() &&
86 VD->getType().getCanonicalType() == ACtx.IntTy;
87 return false;
88 });
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.
98static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) {
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
119void 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());
130}
131
132void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
133 if (!C.inTopFrame())
134 return;
135
136 ASTContext &ACtx = C.getASTContext();
137 ProgramStateRef State = C.getState();
138
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());
146 assert(ErrnoR && "Memory region should exist for the 'errno' variable.");
147 State = State->set<ErrnoRegion>(ErrnoR);
148 State =
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.
158 SValBuilder &SVB = C.getSValBuilder();
159 MemRegionManager &RMgr = C.getStateManager().getRegionManager();
160
161 const MemSpaceRegion *GlobalSystemSpace =
162 RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
163
164 // Create an artifical symbol for the region.
165 // It is not possible to associate a statement or expression in this case.
166 const SymbolConjured *Sym = SVB.conjureSymbol(
167 nullptr, C.getLocationContext(),
168 ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);
169
170 // The symbolic region is untyped, create a typed sub-region in it.
171 // The ElementRegion is used to make the errno region a typed region.
172 const MemRegion *ErrnoR = RMgr.getElementRegion(
173 ACtx.IntTy, SVB.makeZeroArrayIndex(),
174 RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());
175 State = State->set<ErrnoRegion>(ErrnoR);
176 State =
178 C.addTransition(State);
179 }
180}
181
182bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {
183 // Return location of "errno" at a call to an "errno address returning"
184 // function.
185 if (ErrnoLocationCalls.contains(Call)) {
186 ProgramStateRef State = C.getState();
187
188 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
189 if (!ErrnoR)
190 return false;
191
192 State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
193 loc::MemRegionVal{ErrnoR});
194 C.addTransition(State);
195 return true;
196 }
197
198 return false;
199}
200
201void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,
202 SymbolReaper &SR) const {
203 // The special errno region should never garbage collected.
204 if (const auto *ErrnoR = State->get<ErrnoRegion>())
205 SR.markLive(ErrnoR);
206}
207
208namespace clang {
209namespace ento {
210namespace errno_modeling {
211
212std::optional<SVal> getErrnoValue(ProgramStateRef State) {
213 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
214 if (!ErrnoR)
215 return {};
216 QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;
217 return State->getSVal(ErrnoR, IntTy);
218}
219
221 const LocationContext *LCtx, SVal Value,
222 ErrnoCheckState EState) {
223 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
224 if (!ErrnoR)
225 return State;
226 // First set the errno value, the old state is still available at 'checkBind'
227 // or 'checkLocation' for errno value.
228 State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);
229 return State->set<ErrnoState>(EState);
230}
231
233 uint64_t Value, ErrnoCheckState EState) {
234 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
235 if (!ErrnoR)
236 return State;
237 State = State->bindLoc(
238 loc::MemRegionVal{ErrnoR},
239 C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),
240 C.getLocationContext());
241 return State->set<ErrnoState>(EState);
242}
243
244std::optional<Loc> getErrnoLoc(ProgramStateRef State) {
245 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
246 if (!ErrnoR)
247 return {};
248 return loc::MemRegionVal{ErrnoR};
249}
250
252 return State->get<ErrnoState>();
253}
254
256 return State->set<ErrnoState>(EState);
257}
258
260 return setErrnoState(State, Irrelevant);
261}
262
263bool 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;
271}
272
273const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
274 return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
275 const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
276 if (ErrnoR && BR.isInteresting(ErrnoR)) {
277 BR.markNotInteresting(ErrnoR);
278 return Message;
279 }
280 return "";
281 });
282}
283
285 CheckerContext &C) {
286 return setErrnoState(State, MustNotBeChecked);
287}
288
290 NonLoc ErrnoSym) {
291 SValBuilder &SVB = C.getSValBuilder();
292 NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>();
294 SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType())
295 .castAs<DefinedOrUnknownSVal>();
296 State = State->assume(Cond, true);
297 if (!State)
298 return nullptr;
299 return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant);
300}
301
304 const Expr *InvalE) {
305 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
306 if (!ErrnoR)
307 return State;
308 State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(),
309 C.getLocationContext(), false);
310 if (!State)
311 return nullptr;
312 return setErrnoState(State, MustBeChecked);
313}
314
315} // namespace errno_modeling
316} // namespace ento
317} // namespace clang
318
319void ento::registerErrnoModeling(CheckerManager &mgr) {
320 mgr.registerChecker<ErrnoModeling>();
321}
322
323bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {
324 return true;
325}
static const FunctionDecl * getErrnoFunc(ASTContext &ACtx)
Search for a function with a specific name that is used to return a pointer to "errno".
static const VarDecl * getErrnoVar(ASTContext &ACtx)
Store a MemRegion that contains the 'errno' integer value.
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
SourceManager & getSourceManager()
Definition: ASTContext.h:705
TranslationUnitDecl * getTranslationUnitDecl() const
Definition: ASTContext.h:1073
QualType getPointerType(QualType T) const
Return the uniqued reference to the type for a pointer to the specified type.
QualType getLValueReferenceType(QualType T, bool SpelledAsLValue=true) const
Return the uniqued reference to the type for an lvalue reference to the specified type.
IdentifierTable & Idents
Definition: ASTContext.h:644
CanQualType IntTy
Definition: ASTContext.h:1100
lookup_result lookup(DeclarationName Name) const
lookup - Find the declarations (if any) with the given Name in this context.
Definition: DeclBase.cpp:1784
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
This represents one expression.
Definition: Expr.h:110
Represents a function declaration or definition.
Definition: Decl.h:1971
One of these records is kept for each identifier that is lexed.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
A (possibly-)qualified type.
Definition: Type.h:940
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
The top declaration context.
Definition: Decl.h:84
Represents a variable declaration or definition.
Definition: Decl.h:918
ASTContext & getASTContext() override
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
An immutable set of CallDescriptions.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
const ElementRegion * getElementRegion(QualType elementType, NonLoc Idx, const SubRegion *superRegion, ASTContext &Ctx)
getElementRegion - Retrieve the memory region associated with the associated element type,...
Definition: MemRegion.cpp:1159
const GlobalsSpaceRegion * getGlobalsRegion(MemRegion::Kind K=MemRegion::GlobalInternalSpaceRegionKind, const CodeTextRegion *R=nullptr)
getGlobalsRegion - Retrieve the memory region associated with global variables.
Definition: MemRegion.cpp:915
const SymbolicRegion * getSymbolicRegion(SymbolRef Sym, const MemSpaceRegion *MemSpace=nullptr)
Retrieve or create a "symbolic" memory region.
Definition: MemRegion.cpp:1192
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:96
MemSpaceRegion - A memory region that represents a "memory space"; for example, the set of global var...
Definition: MemRegion.h:203
The tag upon which the TagVisitor reacts.
Definition: BugReporter.h:779
const ExplodedNode * getErrorNode() const
Definition: BugReporter.h:402
bool isInteresting(SymbolRef sym) const
const SymbolConjured * conjureSymbol(const Stmt *stmt, const LocationContext *LCtx, QualType type, unsigned visitCount, const void *symbolTag=nullptr)
Definition: SValBuilder.h:174
DefinedOrUnknownSVal makeZeroVal(QualType type)
Construct an SVal representing '0' for the specified type.
Definition: SValBuilder.cpp:62
QualType getConditionType() const
Definition: SValBuilder.h:153
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition: SVals.h:82
A symbol representing the result of an expression in the case when we do not know anything about what...
Definition: SymbolManager.h:79
A class responsible for cleaning up unused symbols.
void markLive(SymbolRef sym)
Unconditionally marks a symbol as live.
std::optional< Loc > getErrnoLoc(ProgramStateRef State)
Returns the location that points to the MemoryRegion where the 'errno' value is stored.
ProgramStateRef setErrnoValue(ProgramStateRef State, const LocationContext *LCtx, SVal Value, ErrnoCheckState EState)
Set value of 'errno' to any SVal, if possible.
bool isErrno(const Decl *D)
Determine if a Decl node related to 'errno'.
ProgramStateRef clearErrnoState(ProgramStateRef State)
Clear state of errno (make it irrelevant).
ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, CheckerContext &C)
Set errno state for the common case when a standard function is successful.
ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState)
Set the errno check state, do not modify the errno value.
const NoteTag * getErrnoNoteTag(CheckerContext &C, const std::string &Message)
Create a NoteTag that displays the message if the 'errno' memory region is marked as interesting,...
ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, CheckerContext &C, const Expr *InvalE)
Set errno state for the common case when a standard function indicates failure only by errno.
ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, NonLoc ErrnoSym)
Set errno state for the common case when a standard function fails.
std::optional< SVal > getErrnoValue(ProgramStateRef State)
Returns the value of 'errno', if 'errno' was found in the AST.
ErrnoCheckState
Describe how reads and writes of errno are handled by the checker.
Definition: ErrnoModeling.h:26
@ MustBeChecked
Value of 'errno' should be checked to find out if a previous function call has failed.
Definition: ErrnoModeling.h:36
@ Irrelevant
We do not know anything about 'errno'.
Definition: ErrnoModeling.h:29
@ MustNotBeChecked
Value of 'errno' is not allowed to be read, it can contain an unspecified value.
Definition: ErrnoModeling.h:42
ErrnoCheckState getErrnoState(ProgramStateRef State)
Returns the errno check state, Errno_Irrelevant if 'errno' was not found (this is not the only case f...
The JSON file list parser is used to communicate input to InstallAPI.