/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ErrnoModeling.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | // 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"}; |
46 | | |
47 | | class ErrnoModeling |
48 | | : public Checker<check::ASTDecl<TranslationUnitDecl>, check::BeginFunction, |
49 | | check::LiveSymbols, eval::Call> { |
50 | | public: |
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; |
56 | | |
57 | | // The declaration of an "errno" variable or "errno location" function. |
58 | | mutable const Decl *ErrnoDecl = nullptr; |
59 | | |
60 | | private: |
61 | | // FIXME: Names from `ErrnoLocationFuncNames` are used to build this set. |
62 | | CallDescriptionSet ErrnoLocationCalls{{{"__errno_location"}, 0, 0}, |
63 | | {{"___errno"}, 0, 0}, |
64 | | {{"__errno"}, 0, 0}, |
65 | | {{"_errno"}, 0, 0}, |
66 | | {{"__error"}, 0, 0}}; |
67 | | }; |
68 | | |
69 | | } // namespace |
70 | | |
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 *) |
74 | | |
75 | | REGISTER_TRAIT_WITH_PROGRAMSTATE(ErrnoState, errno_modeling::ErrnoCheckState) |
76 | | |
77 | | /// Search for a variable called "errno" in the AST. |
78 | | /// Return nullptr if not found. |
79 | 56 | static const VarDecl *getErrnoVar(ASTContext &ACtx) { |
80 | 56 | IdentifierInfo &II = ACtx.Idents.get(ErrnoVarName); |
81 | 56 | auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); |
82 | 56 | auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) { |
83 | 8 | if (auto *VD = dyn_cast<VarDecl>(D)) |
84 | 8 | return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) && |
85 | 8 | VD->hasExternalStorage() && |
86 | 8 | VD->getType().getCanonicalType() == ACtx.IntTy; |
87 | 0 | return false; |
88 | 8 | }); |
89 | 56 | if (Found == LookupRes.end()) |
90 | 48 | return nullptr; |
91 | | |
92 | 8 | return cast<VarDecl>(*Found); |
93 | 56 | } |
94 | | |
95 | | /// Search for a function with a specific name that is used to return a pointer |
96 | | /// to "errno". |
97 | | /// Return nullptr if no such function was found. |
98 | 48 | static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) { |
99 | 48 | SmallVector<const Decl *> LookupRes; |
100 | 240 | for (StringRef ErrnoName : ErrnoLocationFuncNames) { |
101 | 240 | IdentifierInfo &II = ACtx.Idents.get(ErrnoName); |
102 | 240 | llvm::append_range(LookupRes, ACtx.getTranslationUnitDecl()->lookup(&II)); |
103 | 240 | } |
104 | | |
105 | 48 | auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) { |
106 | 4 | if (auto *FD = dyn_cast<FunctionDecl>(D)) |
107 | 4 | return ACtx.getSourceManager().isInSystemHeader(FD->getLocation()) && |
108 | 4 | FD->isExternC() && FD->getNumParams() == 0 && |
109 | 4 | FD->getReturnType().getCanonicalType() == |
110 | 4 | ACtx.getPointerType(ACtx.IntTy); |
111 | 0 | return false; |
112 | 4 | }); |
113 | 48 | if (Found == LookupRes.end()) |
114 | 44 | return nullptr; |
115 | | |
116 | 4 | return cast<FunctionDecl>(*Found); |
117 | 48 | } |
118 | | |
119 | | void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D, |
120 | 56 | 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 | 56 | ErrnoDecl = getErrnoVar(Mgr.getASTContext()); |
128 | 56 | if (!ErrnoDecl) |
129 | 48 | ErrnoDecl = getErrnoFunc(Mgr.getASTContext()); |
130 | 56 | } |
131 | | |
132 | 256 | void ErrnoModeling::checkBeginFunction(CheckerContext &C) const { |
133 | 256 | if (!C.inTopFrame()) |
134 | 24 | return; |
135 | | |
136 | 232 | ASTContext &ACtx = C.getASTContext(); |
137 | 232 | ProgramStateRef State = C.getState(); |
138 | | |
139 | 232 | 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 | 69 | const MemRegion *ErrnoR = |
145 | 69 | State->getRegion(ErrnoVar, C.getLocationContext()); |
146 | 69 | assert(ErrnoR && "Memory region should exist for the 'errno' variable."); |
147 | 69 | State = State->set<ErrnoRegion>(ErrnoR); |
148 | 69 | State = |
149 | 69 | errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant); |
150 | 69 | C.addTransition(State); |
151 | 163 | } else if (ErrnoDecl) { |
152 | 58 | 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 |
157 | | // cases. |
158 | 58 | SValBuilder &SVB = C.getSValBuilder(); |
159 | 58 | MemRegionManager &RMgr = C.getStateManager().getRegionManager(); |
160 | | |
161 | 58 | const MemSpaceRegion *GlobalSystemSpace = |
162 | 58 | RMgr.getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); |
163 | | |
164 | | // Create an artifical symbol for the region. |
165 | | // It is not possible to associate a statement or expression in this case. |
166 | 58 | const SymbolConjured *Sym = SVB.conjureSymbol( |
167 | 58 | nullptr, C.getLocationContext(), |
168 | 58 | ACtx.getLValueReferenceType(ACtx.IntTy), C.blockCount(), &ErrnoDecl); |
169 | | |
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 | 58 | const MemRegion *ErrnoR = RMgr.getElementRegion( |
173 | 58 | ACtx.IntTy, SVB.makeZeroArrayIndex(), |
174 | 58 | RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), C.getASTContext()); |
175 | 58 | State = State->set<ErrnoRegion>(ErrnoR); |
176 | 58 | State = |
177 | 58 | errno_modeling::setErrnoValue(State, C, 0, errno_modeling::Irrelevant); |
178 | 58 | C.addTransition(State); |
179 | 58 | } |
180 | 232 | } |
181 | | |
182 | 894 | bool ErrnoModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { |
183 | | // Return location of "errno" at a call to an "errno address returning" |
184 | | // function. |
185 | 894 | if (ErrnoLocationCalls.contains(Call)) { |
186 | 136 | ProgramStateRef State = C.getState(); |
187 | | |
188 | 136 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
189 | 136 | if (!ErrnoR) |
190 | 0 | return false; |
191 | | |
192 | 136 | State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), |
193 | 136 | loc::MemRegionVal{ErrnoR}); |
194 | 136 | C.addTransition(State); |
195 | 136 | return true; |
196 | 136 | } |
197 | | |
198 | 758 | return false; |
199 | 894 | } |
200 | | |
201 | | void ErrnoModeling::checkLiveSymbols(ProgramStateRef State, |
202 | 4.69k | SymbolReaper &SR) const { |
203 | | // The special errno region should never garbage collected. |
204 | 4.69k | if (const auto *ErrnoR = State->get<ErrnoRegion>()) |
205 | 1.86k | SR.markLive(ErrnoR); |
206 | 4.69k | } |
207 | | |
208 | | namespace clang { |
209 | | namespace ento { |
210 | | namespace errno_modeling { |
211 | | |
212 | 4 | std::optional<SVal> getErrnoValue(ProgramStateRef State) { |
213 | 4 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
214 | 4 | if (!ErrnoR) |
215 | 0 | return {}; |
216 | 4 | QualType IntTy = State->getAnalysisManager().getASTContext().IntTy; |
217 | 4 | return State->getSVal(ErrnoR, IntTy); |
218 | 4 | } |
219 | | |
220 | | ProgramStateRef setErrnoValue(ProgramStateRef State, |
221 | | const LocationContext *LCtx, SVal Value, |
222 | 260 | ErrnoCheckState EState) { |
223 | 260 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
224 | 260 | if (!ErrnoR) |
225 | 170 | return State; |
226 | | // First set the errno value, the old state is still available at 'checkBind' |
227 | | // or 'checkLocation' for errno value. |
228 | 90 | State = State->bindLoc(loc::MemRegionVal{ErrnoR}, Value, LCtx); |
229 | 90 | return State->set<ErrnoState>(EState); |
230 | 260 | } |
231 | | |
232 | | ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, |
233 | 313 | uint64_t Value, ErrnoCheckState EState) { |
234 | 313 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
235 | 313 | if (!ErrnoR) |
236 | 0 | return State; |
237 | 313 | State = State->bindLoc( |
238 | 313 | loc::MemRegionVal{ErrnoR}, |
239 | 313 | C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy), |
240 | 313 | C.getLocationContext()); |
241 | 313 | return State->set<ErrnoState>(EState); |
242 | 313 | } |
243 | | |
244 | 2.15k | std::optional<Loc> getErrnoLoc(ProgramStateRef State) { |
245 | 2.15k | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
246 | 2.15k | if (!ErrnoR) |
247 | 84 | return {}; |
248 | 2.07k | return loc::MemRegionVal{ErrnoR}; |
249 | 2.15k | } |
250 | | |
251 | 502 | ErrnoCheckState getErrnoState(ProgramStateRef State) { |
252 | 502 | return State->get<ErrnoState>(); |
253 | 502 | } |
254 | | |
255 | 1.51k | ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) { |
256 | 1.51k | return State->set<ErrnoState>(EState); |
257 | 1.51k | } |
258 | | |
259 | 600 | ProgramStateRef clearErrnoState(ProgramStateRef State) { |
260 | 600 | return setErrnoState(State, Irrelevant); |
261 | 600 | } |
262 | | |
263 | 331 | bool isErrno(const Decl *D) { |
264 | 331 | if (const auto *VD = dyn_cast_or_null<VarDecl>(D)) |
265 | 0 | if (const IdentifierInfo *II = VD->getIdentifier()) |
266 | 0 | return II->getName() == ErrnoVarName; |
267 | 331 | if (const auto *FD = dyn_cast_or_null<FunctionDecl>(D)) |
268 | 331 | if (const IdentifierInfo *II = FD->getIdentifier()) |
269 | 331 | return llvm::is_contained(ErrnoLocationFuncNames, II->getName()); |
270 | 0 | return false; |
271 | 331 | } |
272 | | |
273 | 432 | const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) { |
274 | 432 | return C.getNoteTag([Message](PathSensitiveBugReport &BR) -> std::string { |
275 | 248 | const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>(); |
276 | 248 | if (ErrnoR && BR.isInteresting(ErrnoR)157 ) { |
277 | 62 | BR.markNotInteresting(ErrnoR); |
278 | 62 | return Message; |
279 | 62 | } |
280 | 186 | return ""; |
281 | 248 | }); |
282 | 432 | } |
283 | | |
284 | | ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, |
285 | 272 | CheckerContext &C) { |
286 | 272 | return setErrnoState(State, MustNotBeChecked); |
287 | 272 | } |
288 | | |
289 | | ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, |
290 | 254 | NonLoc ErrnoSym) { |
291 | 254 | SValBuilder &SVB = C.getSValBuilder(); |
292 | 254 | NonLoc ZeroVal = SVB.makeZeroVal(C.getASTContext().IntTy).castAs<NonLoc>(); |
293 | 254 | DefinedOrUnknownSVal Cond = |
294 | 254 | SVB.evalBinOp(State, BO_NE, ErrnoSym, ZeroVal, SVB.getConditionType()) |
295 | 254 | .castAs<DefinedOrUnknownSVal>(); |
296 | 254 | State = State->assume(Cond, true); |
297 | 254 | if (!State) |
298 | 0 | return nullptr; |
299 | 254 | return setErrnoValue(State, C.getLocationContext(), ErrnoSym, Irrelevant); |
300 | 254 | } |
301 | | |
302 | | ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, |
303 | | CheckerContext &C, |
304 | 12 | const Expr *InvalE) { |
305 | 12 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
306 | 12 | if (!ErrnoR) |
307 | 4 | return State; |
308 | 8 | State = State->invalidateRegions(ErrnoR, InvalE, C.blockCount(), |
309 | 8 | C.getLocationContext(), false); |
310 | 8 | if (!State) |
311 | 0 | return nullptr; |
312 | 8 | return setErrnoState(State, MustBeChecked); |
313 | 8 | } |
314 | | |
315 | 272 | const NoteTag *getNoteTagForStdSuccess(CheckerContext &C, llvm::StringRef Fn) { |
316 | 272 | return getErrnoNoteTag( |
317 | 272 | C, llvm::formatv( |
318 | 272 | "'errno' may be undefined after successful call to '{0}'", Fn)); |
319 | 272 | } |
320 | | |
321 | | const NoteTag *getNoteTagForStdMustBeChecked(CheckerContext &C, |
322 | 12 | llvm::StringRef Fn) { |
323 | 12 | return getErrnoNoteTag( |
324 | 12 | C, llvm::formatv("'{0}' indicates failure only by setting 'errno'", Fn)); |
325 | 12 | } |
326 | | |
327 | | } // namespace errno_modeling |
328 | | } // namespace ento |
329 | | } // namespace clang |
330 | | |
331 | 56 | void ento::registerErrnoModeling(CheckerManager &mgr) { |
332 | 56 | mgr.registerChecker<ErrnoModeling>(); |
333 | 56 | } |
334 | | |
335 | 144 | bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) { |
336 | 144 | return true; |
337 | 144 | } |