clang 22.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"
29#include "llvm/ADT/STLExtras.h"
30#include <optional>
31
32using namespace clang;
33using namespace ento;
34
35namespace {
36
37// Name of the "errno" variable.
38// FIXME: Is there a system where it is not called "errno" but is a variable?
39const char *ErrnoVarName = "errno";
40
41// Names of functions that return a location of the "errno" value.
42// FIXME: Are there other similar function names?
43CallDescriptionSet ErrnoLocationCalls{
44 {CDM::CLibrary, {"__errno_location"}, 0, 0},
45 {CDM::CLibrary, {"___errno"}, 0, 0},
46 {CDM::CLibrary, {"__errno"}, 0, 0},
47 {CDM::CLibrary, {"_errno"}, 0, 0},
48 {CDM::CLibrary, {"__error"}, 0, 0}};
49
50class ErrnoModeling
51 : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
52 check::LiveSymbols, eval::Call> {
53public:
54 void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,
55 BugReporter &BR) const;
56 void checkBeginFunction(CheckerContext &C) const;
57 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
58 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
59
60private:
61 // The declaration of an "errno" variable on systems where errno is
62 // represented by a variable (and not a function that queries its location).
63 mutable const VarDecl *ErrnoDecl = nullptr;
64};
65
66} // namespace
67
68/// Store a MemRegion that contains the 'errno' integer value.
69/// The value is null if the 'errno' value was not recognized in the AST.
71
73
74void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D,
75 AnalysisManager &Mgr, BugReporter &BR) const {
76 // Try to find the declaration of the external variable `int errno;`.
77 // There are also C library implementations, where the `errno` location is
78 // accessed via a function that returns its address; in those environments
79 // this callback has no effect.
80 ASTContext &ACtx = Mgr.getASTContext();
81 IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);
82 auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
83 auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
84 if (auto *VD = dyn_cast<VarDecl>(D))
85 return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&
86 VD->hasExternalStorage() &&
87 VD->getType().getCanonicalType() == ACtx.IntTy;
88 return false;
89 });
90 if (Found != LookupRes.end())
91 ErrnoDecl = cast<VarDecl>(*Found);
92}
93
94void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
95 if (!C.inTopFrame())
96 return;
97
98 ASTContext &ACtx = C.getASTContext();
99 ProgramStateRef State = C.getState();
100
101 const MemRegion *ErrnoR = nullptr;
102
103 if (ErrnoDecl) {
104 // There is an external 'errno' variable, so we can simply use the memory
105 // region that's associated with it.
106 ErrnoR = State->getRegion(ErrnoDecl, C.getLocationContext());
107 assert(ErrnoR && "Memory region should exist for the 'errno' variable.");
108 } else {
109 // There is no 'errno' variable, so create a new symbolic memory region
110 // that can be used to model the return value of the "get the location of
111 // errno" internal functions.
112 // NOTE: this `SVal` is created even if errno is not defined or used.
113 SValBuilder &SVB = C.getSValBuilder();
114 MemRegionManager &RMgr = C.getStateManager().getRegionManager();
115
116 const MemSpaceRegion *GlobalSystemSpace =
117 RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
118
119 // Create an artifical symbol for the region.
120 // Note that it is not possible to associate a statement or expression in
121 // this case and the `symbolTag` (opaque pointer tag) is just the address
122 // of the data member `ErrnoDecl` of the singleton `ErrnoModeling` checker
123 // object.
124 const SymbolConjured *Sym = SVB.conjureSymbol(
125 C.getCFGElementRef(), C.getLocationContext(),
126 ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);
127
128 // The symbolic region is untyped, create a typed sub-region in it.
129 // The ElementRegion is used to make the errno region a typed region.
130 ErrnoR = RMgr.getElementRegion(
131 ACtx.IntTy, SVB.makeZeroArrayIndex(),
132 RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());
133 }
134 assert(ErrnoR);
135 State = State->set<ErrnoRegion>(ErrnoR);
136 State =
138 C.addTransition(State);
139}
140
141bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {
142 // Return location of "errno" at a call to an "errno address returning"
143 // function.
145 ProgramStateRef State = C.getState();
146
147 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
148 if (!ErrnoR)
149 return false;
150
151 State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
152 loc::MemRegionVal{ErrnoR});
153 C.addTransition(State);
154 return true;
155 }
156
157 return false;
158}
159
160void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,
161 SymbolReaper &SR) const {
162 // The special errno region should never garbage collected.
163 if (const auto *ErrnoR = State->get<ErrnoRegion>())
164 SR.markLive(ErrnoR);
165}
166
167namespace clang {
168namespace ento {
169namespace errno_modeling {
170
171std::optional<SVal> getErrnoValue(ProgramStateRef State) {
172 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
173 if (!ErrnoR)
174 return {};
175 QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;
176 return State->getSVal(ErrnoR, IntTy);
177}
178
180 const LocationContext *LCtx, SVal Value,
181 ErrnoCheckState EState) {
182 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
183 if (!ErrnoR)
184 return State;
185 // First set the errno value, the old state is still available at 'checkBind'
186 // or 'checkLocation' for errno value.
187 State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);
188 return State->set<ErrnoState>(EState);
189}
190
192 uint64_t Value, ErrnoCheckState EState) {
193 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
194 if (!ErrnoR)
195 return State;
196 State = State->bindLoc(
197 loc::MemRegionVal{ErrnoR},
198 C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),
199 C.getLocationContext());
200 return State->set<ErrnoState>(EState);
201}
202
203std::optional<Loc> getErrnoLoc(ProgramStateRef State) {
204 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
205 if (!ErrnoR)
206 return {};
207 return loc::MemRegionVal{ErrnoR};
208}
209
211 return State->get<ErrnoState>();
212}
213
215 return State->set<ErrnoState>(EState);
216}
217
221
223 return ErrnoLocationCalls.contains(CE);
224}
225
226const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
227 return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
228 const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
229 if (ErrnoR && BR.isInteresting(ErrnoR)) {
230 BR.markNotInteresting(ErrnoR);
231 return Message;
232 }
233 return "";
234 });
235}
236
241
243 NonLoc ErrnoSym) {
244 SValBuilder &SVB = C.getSValBuilder();
245 NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>();
247 SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType())
249 State = State->assume(Cond, true);
250 if (!State)
251 return nullptr;
252 return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant);
253}
254
257 ConstCFGElementRef Elem) {
258 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
259 if (!ErrnoR)
260 return State;
261 State = State->invalidateRegions(ErrnoR, Elem, C.blockCount(),
262 C.getLocationContext(), false);
263 if (!State)
264 return nullptr;
265 return setErrnoState(State, MustBeChecked);
266}
267
268} // namespace errno_modeling
269} // namespace ento
270} // namespace clang
271
272void ento::registerErrnoModeling(CheckerManager &mgr) {
273 mgr.registerChecker<ErrnoModeling>();
274}
275
276bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {
277 return true;
278}
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
SourceManager & getSourceManager()
Definition ASTContext.h:798
TranslationUnitDecl * getTranslationUnitDecl() const
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:737
CanQualType IntTy
lookup_result lookup(DeclarationName Name) const
lookup - Find the declarations (if any) with the given Name in this context.
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 TypeBase.h:937
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
The top declaration context.
Definition Decl.h:104
ASTContext & getASTContext() override
BugReporter is a utility class for generating PathDiagnostics for analysis.
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)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:553
const ProgramStateRef & getState() const
const ElementRegion * getElementRegion(QualType elementType, NonLoc Idx, const SubRegion *superRegion, const ASTContext &Ctx)
getElementRegion - Retrieve the memory region associated with the associated element type,...
const GlobalsSpaceRegion * getGlobalsRegion(MemRegion::Kind K=MemRegion::GlobalInternalSpaceRegionKind, const CodeTextRegion *R=nullptr)
getGlobalsRegion - Retrieve the memory region associated with global variables.
const SymbolicRegion * getSymbolicRegion(SymbolRef Sym, const MemSpaceRegion *MemSpace=nullptr)
Retrieve or create a "symbolic" memory region.
MemRegion - The root abstract class for all memory regions.
Definition MemRegion.h:98
The tag upon which the TagVisitor reacts.
const ExplodedNode * getErrorNode() const
bool isInteresting(SymbolRef sym) const
DefinedOrUnknownSVal makeZeroVal(QualType type)
Construct an SVal representing '0' for the specified type.
QualType getConditionType() const
SVal evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, SVal lhs, SVal rhs, QualType type)
const SymbolConjured * conjureSymbol(ConstCFGElementRef Elem, const LocationContext *LCtx, QualType type, unsigned visitCount, const void *symbolTag=nullptr)
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition SVals.h:56
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
Definition SVals.h:83
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.
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 setErrnoStdMustBeChecked(ProgramStateRef State, CheckerContext &C, ConstCFGElementRef Elem)
Set errno state for the common case when a standard function indicates failure only by errno.
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,...
bool isErrnoLocationCall(const CallEvent &CE)
Determine if Call is a call to an internal function that returns the location of errno (in environmen...
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.
@ MustBeChecked
Value of 'errno' should be checked to find out if a previous function call has failed.
@ Irrelevant
We do not know anything about 'errno'.
@ MustNotBeChecked
Value of 'errno' is not allowed to be read, it can contain an unspecified value.
ErrnoCheckState getErrnoState(ProgramStateRef State)
Returns the errno check state, Errno_Irrelevant if 'errno' was not found (this is not the only case f...
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
The JSON file list parser is used to communicate input to InstallAPI.
CFGBlock::ConstCFGElementRef ConstCFGElementRef
Definition CFG.h:1199
Expr * Cond
};
U cast(CodeGen::Address addr)
Definition Address.h:327