Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
35294 views
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"
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"
32
#include <optional>
33
34
using namespace clang;
35
using namespace ento;
36
37
namespace {
38
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
43
// Names of functions that return a location of the "errno" value.
44
// FIXME: Are there other similar function names?
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}};
51
52
class ErrnoModeling
53
: public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
54
check::LiveSymbols, eval::Call> {
55
public:
56
void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,
57
BugReporter &BR) const;
58
void checkBeginFunction(CheckerContext &C) const;
59
void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
60
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
61
62
private:
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;
66
};
67
68
} // namespace
69
70
/// Store a MemRegion that contains the 'errno' integer value.
71
/// The value is null if the 'errno' value was not recognized in the AST.
72
REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)
73
74
REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)
75
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();
83
IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);
84
auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
85
auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
86
if (auto *VD = dyn_cast<VarDecl>(D))
87
return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&
88
VD->hasExternalStorage() &&
89
VD->getType().getCanonicalType() == ACtx.IntTy;
90
return false;
91
});
92
if (Found != LookupRes.end())
93
ErrnoDecl = cast<VarDecl>(*Found);
94
}
95
96
void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
97
if (!C.inTopFrame())
98
return;
99
100
ASTContext &ACtx = C.getASTContext();
101
ProgramStateRef State = C.getState();
102
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());
109
assert(ErrnoR && "Memory region should exist for the 'errno' variable.");
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.
115
SValBuilder &SVB = C.getSValBuilder();
116
MemRegionManager &RMgr = C.getStateManager().getRegionManager();
117
118
const MemSpaceRegion *GlobalSystemSpace =
119
RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
120
121
// Create an artifical symbol for the region.
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.
126
const SymbolConjured *Sym = SVB.conjureSymbol(
127
nullptr, C.getLocationContext(),
128
ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);
129
130
// The symbolic region is untyped, create a typed sub-region in it.
131
// The ElementRegion is used to make the errno region a typed region.
132
ErrnoR = RMgr.getElementRegion(
133
ACtx.IntTy, SVB.makeZeroArrayIndex(),
134
RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());
135
}
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);
141
}
142
143
bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {
144
// Return location of "errno" at a call to an "errno address returning"
145
// function.
146
if (errno_modeling::isErrnoLocationCall(Call)) {
147
ProgramStateRef State = C.getState();
148
149
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
150
if (!ErrnoR)
151
return false;
152
153
State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
154
loc::MemRegionVal{ErrnoR});
155
C.addTransition(State);
156
return true;
157
}
158
159
return false;
160
}
161
162
void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,
163
SymbolReaper &SR) const {
164
// The special errno region should never garbage collected.
165
if (const auto *ErrnoR = State->get<ErrnoRegion>())
166
SR.markLive(ErrnoR);
167
}
168
169
namespace clang {
170
namespace ento {
171
namespace errno_modeling {
172
173
std::optional<SVal> getErrnoValue(ProgramStateRef State) {
174
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
175
if (!ErrnoR)
176
return {};
177
QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;
178
return State->getSVal(ErrnoR, IntTy);
179
}
180
181
ProgramStateRef setErrnoValue(ProgramStateRef State,
182
const LocationContext *LCtx, SVal Value,
183
ErrnoCheckState EState) {
184
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
185
if (!ErrnoR)
186
return State;
187
// First set the errno value, the old state is still available at 'checkBind'
188
// or 'checkLocation' for errno value.
189
State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);
190
return State->set<ErrnoState>(EState);
191
}
192
193
ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
194
uint64_t Value, ErrnoCheckState EState) {
195
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
196
if (!ErrnoR)
197
return State;
198
State = State->bindLoc(
199
loc::MemRegionVal{ErrnoR},
200
C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),
201
C.getLocationContext());
202
return State->set<ErrnoState>(EState);
203
}
204
205
std::optional<Loc> getErrnoLoc(ProgramStateRef State) {
206
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
207
if (!ErrnoR)
208
return {};
209
return loc::MemRegionVal{ErrnoR};
210
}
211
212
ErrnoCheckState getErrnoState(ProgramStateRef State) {
213
return State->get<ErrnoState>();
214
}
215
216
ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {
217
return State->set<ErrnoState>(EState);
218
}
219
220
ProgramStateRef clearErrnoState(ProgramStateRef State) {
221
return setErrnoState(State, Irrelevant);
222
}
223
224
bool isErrnoLocationCall(const CallEvent &CE) {
225
return ErrnoLocationCalls.contains(CE);
226
}
227
228
const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
229
return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
230
const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
231
if (ErrnoR && BR.isInteresting(ErrnoR)) {
232
BR.markNotInteresting(ErrnoR);
233
return Message;
234
}
235
return "";
236
});
237
}
238
239
ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State,
240
CheckerContext &C) {
241
return setErrnoState(State, MustNotBeChecked);
242
}
243
244
ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C,
245
NonLoc ErrnoSym) {
246
SValBuilder &SVB = C.getSValBuilder();
247
NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>();
248
DefinedOrUnknownSVal Cond =
249
SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType())
250
.castAs<DefinedOrUnknownSVal>();
251
State = State->assume(Cond, true);
252
if (!State)
253
return nullptr;
254
return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant);
255
}
256
257
ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State,
258
CheckerContext &C,
259
const Expr *InvalE) {
260
const MemRegion *ErrnoR = State->get<ErrnoRegion>();
261
if (!ErrnoR)
262
return State;
263
State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(),
264
C.getLocationContext(), false);
265
if (!State)
266
return nullptr;
267
return setErrnoState(State, MustBeChecked);
268
}
269
270
} // namespace errno_modeling
271
} // namespace ento
272
} // namespace clang
273
274
void ento::registerErrnoModeling(CheckerManager &mgr) {
275
mgr.registerChecker<ErrnoModeling>();
276
}
277
278
bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {
279
return true;
280
}
281
282