1 //=== ErrnoModeling.cpp -----------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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.
19 //===----------------------------------------------------------------------===//
21 #include "ErrnoModeling.h"
22 #include "clang/AST/ParentMapContext.h"
23 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
24 #include "clang/StaticAnalyzer/Core/Checker.h"
25 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
29 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
30 #include "llvm/ADT/STLExtras.h"
31 #include "llvm/Support/FormatVariadic.h"
34 using namespace clang
;
39 // Name of the "errno" variable.
40 // FIXME: Is there a system where it is not called "errno" but is a variable?
41 const char *ErrnoVarName
= "errno";
42 // Names of functions that return a location of the "errno" value.
43 // FIXME: Are there other similar function names?
44 const char *ErrnoLocationFuncNames
[] = {"__errno_location", "___errno",
45 "__errno", "_errno", "__error"};
48 : public Checker
<check::ASTDecl
<TranslationUnitDecl
>, check::BeginFunction
,
49 check::LiveSymbols
, eval::Call
> {
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;
57 // The declaration of an "errno" variable or "errno location" function.
58 mutable const Decl
*ErrnoDecl
= nullptr;
61 // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set.
62 CallDescriptionSet ErrnoLocationCalls
{{{"__errno_location"}, 0, 0},
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.
73 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion
, const MemRegion
*)
75 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState
, errno_modeling::ErrnoCheckState
)
77 /// Search for a variable called "errno" in the AST.
78 /// Return nullptr if not found.
79 static 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
;
89 if (Found
== LookupRes
.end())
92 return cast
<VarDecl
>(*Found
);
95 /// Search for a function with a specific name that is used to return a pointer
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
));
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
);
113 if (Found
== LookupRes
.end())
116 return cast
<FunctionDecl
>(*Found
);
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());
129 ErrnoDecl
= getErrnoFunc(Mgr
.getASTContext());
132 void ErrnoModeling::checkBeginFunction(CheckerContext
&C
) const {
136 ASTContext
&ACtx
= C
.getASTContext();
137 ProgramStateRef State
= C
.getState();
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
);
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
158 SValBuilder
&SVB
= C
.getSValBuilder();
159 MemRegionManager
&RMgr
= C
.getStateManager().getRegionManager();
161 const MemSpaceRegion
*GlobalSystemSpace
=
162 RMgr
.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind
);
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
);
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
);
177 errno_modeling::setErrnoValue(State
, C
, 0, errno_modeling::Irrelevant
);
178 C
.addTransition(State
);
182 bool ErrnoModeling::evalCall(const CallEvent
&Call
, CheckerContext
&C
) const {
183 // Return location of "errno" at a call to an "errno address returning"
185 if (ErrnoLocationCalls
.contains(Call
)) {
186 ProgramStateRef State
= C
.getState();
188 const MemRegion
*ErrnoR
= State
->get
<ErrnoRegion
>();
192 State
= State
->BindExpr(Call
.getOriginExpr(), C
.getLocationContext(),
193 loc::MemRegionVal
{ErrnoR
});
194 C
.addTransition(State
);
201 void 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
>())
210 namespace errno_modeling
{
212 std::optional
<SVal
> getErrnoValue(ProgramStateRef State
) {
213 const MemRegion
*ErrnoR
= State
->get
<ErrnoRegion
>();
216 QualType IntTy
= State
->getAnalysisManager().getASTContext().IntTy
;
217 return State
->getSVal(ErrnoR
, IntTy
);
220 ProgramStateRef
setErrnoValue(ProgramStateRef State
,
221 const LocationContext
*LCtx
, SVal Value
,
222 ErrnoCheckState EState
) {
223 const MemRegion
*ErrnoR
= State
->get
<ErrnoRegion
>();
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
);
232 ProgramStateRef
setErrnoValue(ProgramStateRef State
, CheckerContext
&C
,
233 uint64_t Value
, ErrnoCheckState EState
) {
234 const MemRegion
*ErrnoR
= State
->get
<ErrnoRegion
>();
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
);
244 std::optional
<Loc
> getErrnoLoc(ProgramStateRef State
) {
245 const MemRegion
*ErrnoR
= State
->get
<ErrnoRegion
>();
248 return loc::MemRegionVal
{ErrnoR
};
251 ErrnoCheckState
getErrnoState(ProgramStateRef State
) {
252 return State
->get
<ErrnoState
>();
255 ProgramStateRef
setErrnoState(ProgramStateRef State
, ErrnoCheckState EState
) {
256 return State
->set
<ErrnoState
>(EState
);
259 ProgramStateRef
clearErrnoState(ProgramStateRef State
) {
260 return setErrnoState(State
, Irrelevant
);
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());
273 const 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
);
284 ProgramStateRef
setErrnoForStdSuccess(ProgramStateRef State
,
286 return setErrnoState(State
, MustNotBeChecked
);
289 ProgramStateRef
setErrnoForStdFailure(ProgramStateRef State
, CheckerContext
&C
,
291 SValBuilder
&SVB
= C
.getSValBuilder();
292 NonLoc ZeroVal
= SVB
.makeZeroVal(C
.getASTContext().IntTy
).castAs
<NonLoc
>();
293 DefinedOrUnknownSVal Cond
=
294 SVB
.evalBinOp(State
, BO_NE
, ErrnoSym
, ZeroVal
, SVB
.getConditionType())
295 .castAs
<DefinedOrUnknownSVal
>();
296 State
= State
->assume(Cond
, true);
299 return setErrnoValue(State
, C
.getLocationContext(), ErrnoSym
, Irrelevant
);
302 ProgramStateRef
setErrnoStdMustBeChecked(ProgramStateRef State
,
304 const Expr
*InvalE
) {
305 const MemRegion
*ErrnoR
= State
->get
<ErrnoRegion
>();
308 State
= State
->invalidateRegions(ErrnoR
, InvalE
, C
.blockCount(),
309 C
.getLocationContext(), false);
312 return setErrnoState(State
, MustBeChecked
);
315 const NoteTag
*getNoteTagForStdSuccess(CheckerContext
&C
, llvm::StringRef Fn
) {
316 return getErrnoNoteTag(
318 "'errno' may be undefined after successful call to '{0}'", Fn
));
321 const NoteTag
*getNoteTagForStdMustBeChecked(CheckerContext
&C
,
322 llvm::StringRef Fn
) {
323 return getErrnoNoteTag(
324 C
, llvm::formatv("'{0}' indicates failure only by setting 'errno'", Fn
));
327 } // namespace errno_modeling
331 void ento::registerErrnoModeling(CheckerManager
&mgr
) {
332 mgr
.registerChecker
<ErrnoModeling
>();
335 bool ento::shouldRegisterErrnoModeling(const CheckerManager
&mgr
) {