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
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
43// Names of functions that return a location of the "errno" value.
44// FIXME: Are there other similar function names?
45CallDescriptionSet 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
52class ErrnoModeling
53 : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
54 check::LiveSymbols, eval::Call> {
55public:
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
62private:
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.
72REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)
73
74REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)
75
76void 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(Name: ErrnoVarName);
84 auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(Name: &II);
85 auto Found = llvm::find_if(Range&: LookupRes, P: [&ACtx](const Decl *D) {
86 if (auto *VD = dyn_cast<VarDecl>(Val: D))
87 return ACtx.getSourceManager().isInSystemHeader(Loc: VD->getLocation()) &&
88 VD->hasExternalStorage() &&
89 VD->getType().getCanonicalType() == ACtx.IntTy;
90 return false;
91 });
92 if (Found != LookupRes.end())
93 ErrnoDecl = cast<VarDecl>(Val: *Found);
94}
95
96void 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(D: ErrnoDecl, LC: 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(K: 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 stmt: nullptr, LCtx: C.getLocationContext(),
128 type: ACtx.getLValueReferenceType(T: ACtx.IntTy), visitCount: C.blockCount(), symbolTag: &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 elementType: ACtx.IntTy, Idx: SVB.makeZeroArrayIndex(),
134 superRegion: RMgr.getSymbolicRegion(Sym, MemSpace: GlobalSystemSpace), Ctx: C.getASTContext());
135 }
136 assert(ErrnoR);
137 State = State->set<ErrnoRegion>(ErrnoR);
138 State =
139 errno_modeling::setErrnoValue(State, C, Value: 0, EState: errno_modeling::Irrelevant);
140 C.addTransition(State);
141}
142
143bool 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(S: Call.getOriginExpr(), LCtx: C.getLocationContext(),
154 V: loc::MemRegionVal{ErrnoR});
155 C.addTransition(State);
156 return true;
157 }
158
159 return false;
160}
161
162void 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(region: ErrnoR);
167}
168
169namespace clang {
170namespace ento {
171namespace errno_modeling {
172
173std::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(R: ErrnoR, T: IntTy);
179}
180
181ProgramStateRef 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(location: loc::MemRegionVal{ErrnoR}, V: Value, LCtx);
190 return State->set<ErrnoState>(EState);
191}
192
193ProgramStateRef 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 location: loc::MemRegionVal{ErrnoR},
200 V: C.getSValBuilder().makeIntVal(integer: Value, type: C.getASTContext().IntTy),
201 LCtx: C.getLocationContext());
202 return State->set<ErrnoState>(EState);
203}
204
205std::optional<Loc> getErrnoLoc(ProgramStateRef State) {
206 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
207 if (!ErrnoR)
208 return {};
209 return loc::MemRegionVal{ErrnoR};
210}
211
212ErrnoCheckState getErrnoState(ProgramStateRef State) {
213 return State->get<ErrnoState>();
214}
215
216ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {
217 return State->set<ErrnoState>(EState);
218}
219
220ProgramStateRef clearErrnoState(ProgramStateRef State) {
221 return setErrnoState(State, EState: Irrelevant);
222}
223
224bool isErrnoLocationCall(const CallEvent &CE) {
225 return ErrnoLocationCalls.contains(Call: CE);
226}
227
228const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
229 return C.getNoteTag(Cb: [Message](PathSensitiveBugReport &BR) -> std::string {
230 const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
231 if (ErrnoR && BR.isInteresting(R: ErrnoR)) {
232 BR.markNotInteresting(R: ErrnoR);
233 return Message;
234 }
235 return "";
236 });
237}
238
239ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State,
240 CheckerContext &C) {
241 return setErrnoState(State, EState: MustNotBeChecked);
242}
243
244ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C,
245 NonLoc ErrnoSym) {
246 SValBuilder &SVB = C.getSValBuilder();
247 NonLoc ZeroVal = SVB.makeZeroVal(type: C.getASTContext().IntTy).castAs<NonLoc>();
248 DefinedOrUnknownSVal Cond =
249 SVB.evalBinOp(state: State, op: BO_NE, lhs: ErrnoSym, rhs: ZeroVal, type: SVB.getConditionType())
250 .castAs<DefinedOrUnknownSVal>();
251 State = State->assume(Cond, Assumption: true);
252 if (!State)
253 return nullptr;
254 return setErrnoValue(State, LCtx: C.getLocationContext(), Value: ErrnoSym, EState: Irrelevant);
255}
256
257ProgramStateRef 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(Regions: ErrnoR, E: InvalE, BlockCount: C.blockCount(),
264 LCtx: C.getLocationContext(), CausesPointerEscape: false);
265 if (!State)
266 return nullptr;
267 return setErrnoState(State, EState: MustBeChecked);
268}
269
270} // namespace errno_modeling
271} // namespace ento
272} // namespace clang
273
274void ento::registerErrnoModeling(CheckerManager &mgr) {
275 mgr.registerChecker<ErrnoModeling>();
276}
277
278bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {
279 return true;
280}
281