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
32 using namespace clang;
33 using namespace ento;
34
35 namespace {
36
37 // Name of the "errno" variable.
38 // FIXME: Is there a system where it is not called "errno" but is a variable?
39 const char *ErrnoVarName = "errno";
40 // Names of functions that return a location of the "errno" value.
41 // FIXME: Are there other similar function names?
42 const char *ErrnoLocationFuncNames[] = {"__errno_location", "___errno",
43 "__errno", "_errno", "__error"};
44
45 class ErrnoModeling
46 : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction,
47 check::LiveSymbols, eval::Call> {
48 public:
49 void checkASTDecl(const TranslationUnitDecl *D, AnalysisManager &Mgr,
50 BugReporter &BR) const;
51 void checkBeginFunction(CheckerContext &C) const;
52 void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
53 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
54
55 // The declaration of an "errno" variable or "errno location" function.
56 mutable const Decl *ErrnoDecl = nullptr;
57
58 private:
59 // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set.
60 CallDescriptionSet ErrnoLocationCalls{{"__errno_location", 0, 0},
61 {"___errno", 0, 0},
62 {"__errno", 0, 0},
63 {"_errno", 0, 0},
64 {"__error", 0, 0}};
65 };
66
67 } // namespace
68
69 /// Store a MemRegion that contains the 'errno' integer value.
70 /// The value is null if the 'errno' value was not recognized in the AST.
REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion,const MemRegion *)71 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoRegion, const MemRegion *)
72
73 REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState)
74
75 /// Search for a variable called "errno" in the AST.
76 /// Return nullptr if not found.
77 static const VarDecl *getErrnoVar(ASTContext &ACtx) {
78 IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName);
79 auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II);
80 auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
81 if (auto *VD = dyn_cast<VarDecl>(D))
82 return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) &&
83 VD->hasExternalStorage() &&
84 VD->getType().getCanonicalType() == ACtx.IntTy;
85 return false;
86 });
87 if (Found == LookupRes.end())
88 return nullptr;
89
90 return cast<VarDecl>(*Found);
91 }
92
93 /// Search for a function with a specific name that is used to return a pointer
94 /// to "errno".
95 /// Return nullptr if no such function was found.
getErrnoFunc(ASTContext & ACtx)96 static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) {
97 SmallVector<const Decl *> LookupRes;
98 for (StringRef ErrnoName : ErrnoLocationFuncNames) {
99 IdentifierInfo &II = ACtx.Idents.get(ErrnoName);
100 llvm::append_range(LookupRes, ACtx.getTranslationUnitDecl()->lookup(&II));
101 }
102
103 auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) {
104 if (auto *FD = dyn_cast<FunctionDecl>(D))
105 return ACtx.getSourceManager().isInSystemHeader(FD->getLocation()) &&
106 FD->isExternC() && FD->getNumParams() == 0 &&
107 FD->getReturnType().getCanonicalType() ==
108 ACtx.getPointerType(ACtx.IntTy);
109 return false;
110 });
111 if (Found == LookupRes.end())
112 return nullptr;
113
114 return cast<FunctionDecl>(*Found);
115 }
116
checkASTDecl(const TranslationUnitDecl * D,AnalysisManager & Mgr,BugReporter & BR) const117 void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D,
118 AnalysisManager &Mgr, BugReporter &BR) const {
119 // Try to find an usable `errno` value.
120 // It can be an external variable called "errno" or a function that returns a
121 // pointer to the "errno" value. This function can have different names.
122 // The actual case is dependent on the C library implementation, we
123 // can only search for a match in one of these variations.
124 // We assume that exactly one of these cases might be true.
125 ErrnoDecl = getErrnoVar(Mgr.getASTContext());
126 if (!ErrnoDecl)
127 ErrnoDecl = getErrnoFunc(Mgr.getASTContext());
128 }
129
checkBeginFunction(CheckerContext & C) const130 void ErrnoModeling::checkBeginFunction(CheckerContext &C) const {
131 if (!C.inTopFrame())
132 return;
133
134 ASTContext &ACtx = C.getASTContext();
135 ProgramStateRef State = C.getState();
136
137 if (const auto *ErrnoVar = dyn_cast_or_null<VarDecl>(ErrnoDecl)) {
138 // There is an external 'errno' variable.
139 // Use its memory region.
140 // The memory region for an 'errno'-like variable is allocated in system
141 // space by MemRegionManager.
142 const MemRegion *ErrnoR =
143 State->getRegion(ErrnoVar, C.getLocationContext());
144 assert(ErrnoR && "Memory region should exist for the 'errno' variable.");
145 State = State->set<ErrnoRegion>(ErrnoR);
146 State =
147 errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);
148 C.addTransition(State);
149 } else if (ErrnoDecl) {
150 assert(isa<FunctionDecl>(ErrnoDecl) && "Invalid errno location function.");
151 // There is a function that returns the location of 'errno'.
152 // We must create a memory region for it in system space.
153 // Currently a symbolic region is used with an artifical symbol.
154 // FIXME: It is better to have a custom (new) kind of MemRegion for such
155 // cases.
156 SValBuilder &SVB = C.getSValBuilder();
157 MemRegionManager &RMgr = C.getStateManager().getRegionManager();
158
159 const MemSpaceRegion *GlobalSystemSpace =
160 RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
161
162 // Create an artifical symbol for the region.
163 // It is not possible to associate a statement or expression in this case.
164 const SymbolConjured *Sym = SVB.conjureSymbol(
165 nullptr, C.getLocationContext(),
166 ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl);
167
168 // The symbolic region is untyped, create a typed sub-region in it.
169 // The ElementRegion is used to make the errno region a typed region.
170 const MemRegion *ErrnoR = RMgr.getElementRegion(
171 ACtx.IntTy, SVB.makeZeroArrayIndex(),
172 RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext());
173 State = State->set<ErrnoRegion>(ErrnoR);
174 State =
175 errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant);
176 C.addTransition(State);
177 }
178 }
179
evalCall(const CallEvent & Call,CheckerContext & C) const180 bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const {
181 // Return location of "errno" at a call to an "errno address returning"
182 // function.
183 if (ErrnoLocationCalls.contains(Call)) {
184 ProgramStateRef State = C.getState();
185
186 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
187 if (!ErrnoR)
188 return false;
189
190 State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
191 loc::MemRegionVal{ErrnoR});
192 C.addTransition(State);
193 return true;
194 }
195
196 return false;
197 }
198
checkLiveSymbols(ProgramStateRef State,SymbolReaper & SR) const199 void ErrnoModeling::checkLiveSymbols(ProgramStateRef State,
200 SymbolReaper &SR) const {
201 // The special errno region should never garbage collected.
202 if (const auto *ErrnoR = State->get<ErrnoRegion>())
203 SR.markLive(ErrnoR);
204 }
205
206 namespace clang {
207 namespace ento {
208 namespace errno_modeling {
209
getErrnoValue(ProgramStateRef State)210 Optional<SVal> getErrnoValue(ProgramStateRef State) {
211 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
212 if (!ErrnoR)
213 return {};
214 QualType IntTy = State->getAnalysisManager().getASTContext().IntTy;
215 return State->getSVal(ErrnoR, IntTy);
216 }
217
setErrnoValue(ProgramStateRef State,const LocationContext * LCtx,SVal Value,ErrnoCheckState EState)218 ProgramStateRef setErrnoValue(ProgramStateRef State,
219 const LocationContext *LCtx, SVal Value,
220 ErrnoCheckState EState) {
221 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
222 if (!ErrnoR)
223 return State;
224 // First set the errno value, the old state is still available at 'checkBind'
225 // or 'checkLocation' for errno value.
226 State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx);
227 return State->set<ErrnoState>(EState);
228 }
229
setErrnoValue(ProgramStateRef State,CheckerContext & C,uint64_t Value,ErrnoCheckState EState)230 ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C,
231 uint64_t Value, ErrnoCheckState EState) {
232 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
233 if (!ErrnoR)
234 return State;
235 State = State->bindLoc(
236 loc::MemRegionVal{ErrnoR},
237 C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy),
238 C.getLocationContext());
239 return State->set<ErrnoState>(EState);
240 }
241
getErrnoLoc(ProgramStateRef State)242 Optional<Loc> getErrnoLoc(ProgramStateRef State) {
243 const MemRegion *ErrnoR = State->get<ErrnoRegion>();
244 if (!ErrnoR)
245 return {};
246 return loc::MemRegionVal{ErrnoR};
247 }
248
setErrnoState(ProgramStateRef State,ErrnoCheckState EState)249 ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) {
250 return State->set<ErrnoState>(EState);
251 }
252
getErrnoState(ProgramStateRef State)253 ErrnoCheckState getErrnoState(ProgramStateRef State) {
254 return State->get<ErrnoState>();
255 }
256
isErrno(const Decl * D)257 bool isErrno(const Decl *D) {
258 if (const auto *VD = dyn_cast_or_null<VarDecl>(D))
259 if (const IdentifierInfo *II = VD->getIdentifier())
260 return II->getName() == ErrnoVarName;
261 if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D))
262 if (const IdentifierInfo *II = FD->getIdentifier())
263 return llvm::is_contained(ErrnoLocationFuncNames, II->getName());
264 return false;
265 }
266
getErrnoNoteTag(CheckerContext & C,const std::string & Message)267 const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) {
268 return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string {
269 const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>();
270 if (ErrnoR && BR.isInteresting(ErrnoR)) {
271 BR.markNotInteresting(ErrnoR);
272 return Message;
273 }
274 return "";
275 });
276 }
277
278 } // namespace errno_modeling
279 } // namespace ento
280 } // namespace clang
281
registerErrnoModeling(CheckerManager & mgr)282 void ento::registerErrnoModeling(CheckerManager &mgr) {
283 mgr.registerChecker<ErrnoModeling>();
284 }
285
shouldRegisterErrnoModeling(const CheckerManager & mgr)286 bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) {
287 return true;
288 }
289