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 | static const VarDecl *getErrnoVar(ASTContext &ACtx) { |
80 | IdentifierInfo &II = ACtx.Idents.get(Name: ErrnoVarName); |
81 | auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); |
82 | auto Found = llvm::find_if(LookupRes, [&ACtx](const Decl *D) { |
83 | if (auto *VD = dyn_cast<VarDecl>(D)) |
84 | return ACtx.getSourceManager().isInSystemHeader(VD->getLocation()) && |
85 | VD->hasExternalStorage() && |
86 | VD->getType().getCanonicalType() == ACtx.IntTy; |
87 | return false; |
88 | }); |
89 | if (Found == LookupRes.end()) |
90 | return nullptr; |
91 | |
92 | return cast<VarDecl>(*Found); |
93 | } |
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 | static const FunctionDecl *getErrnoFunc(ASTContext &ACtx) { |
99 | SmallVector<const Decl *> LookupRes; |
100 | for (StringRef ErrnoName : ErrnoLocationFuncNames) { |
101 | IdentifierInfo &II = ACtx.Idents.get(Name: ErrnoName); |
102 | llvm::append_range(LookupRes, ACtx.getTranslationUnitDecl()->lookup(&II)); |
103 | } |
104 | |
105 | auto Found = llvm::find_if(Range&: LookupRes, P: [&ACtx](const Decl *D) { |
106 | if (auto *FD = dyn_cast<FunctionDecl>(D)) |
107 | return ACtx.getSourceManager().isInSystemHeader(FD->getLocation()) && |
108 | FD->isExternC() && FD->getNumParams() == 0 && |
109 | FD->getReturnType().getCanonicalType() == |
110 | ACtx.getPointerType(ACtx.IntTy); |
111 | return false; |
112 | }); |
113 | if (Found == LookupRes.end()) |
114 | return nullptr; |
115 | |
116 | return cast<FunctionDecl>(Val: *Found); |
117 | } |
118 | |
119 | void ErrnoModeling::checkASTDecl(const TranslationUnitDecl *D, |
120 | 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 | ErrnoDecl = getErrnoVar(ACtx&: Mgr.getASTContext()); |
128 | if (!ErrnoDecl) |
129 | ErrnoDecl = getErrnoFunc(ACtx&: Mgr.getASTContext()); |
130 | } |
131 | |
132 | void ErrnoModeling::checkBeginFunction(CheckerContext &C) const { |
133 | if (!C.inTopFrame()) |
134 | return; |
135 | |
136 | ASTContext &ACtx = C.getASTContext(); |
137 | ProgramStateRef State = C.getState(); |
138 | |
139 | if (const auto *ErrnoVar = dyn_cast_or_null<VarDecl>(Val: 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 | const MemRegion *ErrnoR = |
145 | State->getRegion(D: ErrnoVar, LC: C.getLocationContext()); |
146 | assert(ErrnoR && "Memory region should exist for the 'errno' variable." ); |
147 | State = State->set<ErrnoRegion>(ErrnoR); |
148 | State = |
149 | errno_modeling::setErrnoValue(State, C, Value: 0, EState: errno_modeling::Irrelevant); |
150 | C.addTransition(State); |
151 | } else if (ErrnoDecl) { |
152 | 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 | SValBuilder &SVB = C.getSValBuilder(); |
159 | MemRegionManager &RMgr = C.getStateManager().getRegionManager(); |
160 | |
161 | const MemSpaceRegion *GlobalSystemSpace = |
162 | RMgr.getGlobalsRegion(K: 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 | const SymbolConjured *Sym = SVB.conjureSymbol( |
167 | nullptr, C.getLocationContext(), |
168 | ACtx.getLValueReferenceType(T: 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 | const MemRegion *ErrnoR = RMgr.getElementRegion( |
173 | elementType: ACtx.IntTy, Idx: SVB.makeZeroArrayIndex(), |
174 | superRegion: RMgr.getSymbolicRegion(Sym, GlobalSystemSpace), Ctx&: C.getASTContext()); |
175 | State = State->set<ErrnoRegion>(ErrnoR); |
176 | State = |
177 | errno_modeling::setErrnoValue(State, C, Value: 0, EState: errno_modeling::Irrelevant); |
178 | C.addTransition(State); |
179 | } |
180 | } |
181 | |
182 | 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 | if (ErrnoLocationCalls.contains(Call)) { |
186 | ProgramStateRef State = C.getState(); |
187 | |
188 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
189 | if (!ErrnoR) |
190 | return false; |
191 | |
192 | State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), |
193 | loc::MemRegionVal{ErrnoR}); |
194 | C.addTransition(State); |
195 | return true; |
196 | } |
197 | |
198 | return false; |
199 | } |
200 | |
201 | void ErrnoModeling::checkLiveSymbols(ProgramStateRef State, |
202 | SymbolReaper &SR) const { |
203 | // The special errno region should never garbage collected. |
204 | if (const auto *ErrnoR = State->get<ErrnoRegion>()) |
205 | SR.markLive(region: ErrnoR); |
206 | } |
207 | |
208 | namespace clang { |
209 | namespace ento { |
210 | namespace errno_modeling { |
211 | |
212 | std::optional<SVal> getErrnoValue(ProgramStateRef State) { |
213 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
214 | if (!ErrnoR) |
215 | return {}; |
216 | QualType IntTy = State->getAnalysisManager().getASTContext().IntTy; |
217 | return State->getSVal(R: ErrnoR, T: IntTy); |
218 | } |
219 | |
220 | ProgramStateRef setErrnoValue(ProgramStateRef State, |
221 | const LocationContext *LCtx, SVal Value, |
222 | ErrnoCheckState EState) { |
223 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
224 | if (!ErrnoR) |
225 | return State; |
226 | // First set the errno value, the old state is still available at 'checkBind' |
227 | // or 'checkLocation' for errno value. |
228 | State = State->bindLoc(location: loc::MemRegionVal{ErrnoR}, V: Value, LCtx); |
229 | return State->set<ErrnoState>(EState); |
230 | } |
231 | |
232 | ProgramStateRef setErrnoValue(ProgramStateRef State, CheckerContext &C, |
233 | uint64_t Value, ErrnoCheckState EState) { |
234 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
235 | if (!ErrnoR) |
236 | return State; |
237 | State = State->bindLoc( |
238 | loc::MemRegionVal{ErrnoR}, |
239 | C.getSValBuilder().makeIntVal(Value, C.getASTContext().IntTy), |
240 | C.getLocationContext()); |
241 | return State->set<ErrnoState>(EState); |
242 | } |
243 | |
244 | std::optional<Loc> getErrnoLoc(ProgramStateRef State) { |
245 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
246 | if (!ErrnoR) |
247 | return {}; |
248 | return loc::MemRegionVal{ErrnoR}; |
249 | } |
250 | |
251 | ErrnoCheckState getErrnoState(ProgramStateRef State) { |
252 | return State->get<ErrnoState>(); |
253 | } |
254 | |
255 | ProgramStateRef setErrnoState(ProgramStateRef State, ErrnoCheckState EState) { |
256 | return State->set<ErrnoState>(EState); |
257 | } |
258 | |
259 | ProgramStateRef clearErrnoState(ProgramStateRef State) { |
260 | return setErrnoState(State, EState: Irrelevant); |
261 | } |
262 | |
263 | bool isErrno(const Decl *D) { |
264 | if (const auto *VD = dyn_cast_or_null<VarDecl>(Val: D)) |
265 | if (const IdentifierInfo *II = VD->getIdentifier()) |
266 | return II->getName() == ErrnoVarName; |
267 | if (const auto *FD = dyn_cast_or_null<FunctionDecl>(Val: D)) |
268 | if (const IdentifierInfo *II = FD->getIdentifier()) |
269 | return llvm::is_contained(Range&: ErrnoLocationFuncNames, Element: II->getName()); |
270 | return false; |
271 | } |
272 | |
273 | const NoteTag *getErrnoNoteTag(CheckerContext &C, const std::string &Message) { |
274 | return C.getNoteTag(Cb: [Message](PathSensitiveBugReport &BR) -> std::string { |
275 | const MemRegion *ErrnoR = BR.getErrorNode()->getState()->get<ErrnoRegion>(); |
276 | if (ErrnoR && BR.isInteresting(R: ErrnoR)) { |
277 | BR.markNotInteresting(R: ErrnoR); |
278 | return Message; |
279 | } |
280 | return "" ; |
281 | }); |
282 | } |
283 | |
284 | ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, |
285 | CheckerContext &C) { |
286 | return setErrnoState(State, EState: MustNotBeChecked); |
287 | } |
288 | |
289 | ProgramStateRef setErrnoForStdFailure(ProgramStateRef State, CheckerContext &C, |
290 | NonLoc ErrnoSym) { |
291 | SValBuilder &SVB = C.getSValBuilder(); |
292 | NonLoc ZeroVal = SVB.makeZeroVal(type: C.getASTContext().IntTy).castAs<NonLoc>(); |
293 | DefinedOrUnknownSVal Cond = |
294 | SVB.evalBinOp(state: State, op: BO_NE, lhs: ErrnoSym, rhs: ZeroVal, type: SVB.getConditionType()) |
295 | .castAs<DefinedOrUnknownSVal>(); |
296 | State = State->assume(Cond, Assumption: true); |
297 | if (!State) |
298 | return nullptr; |
299 | return setErrnoValue(State, LCtx: C.getLocationContext(), Value: ErrnoSym, EState: Irrelevant); |
300 | } |
301 | |
302 | ProgramStateRef setErrnoStdMustBeChecked(ProgramStateRef State, |
303 | CheckerContext &C, |
304 | const Expr *InvalE) { |
305 | const MemRegion *ErrnoR = State->get<ErrnoRegion>(); |
306 | if (!ErrnoR) |
307 | return State; |
308 | State = State->invalidateRegions(Regions: ErrnoR, E: InvalE, BlockCount: C.blockCount(), |
309 | LCtx: C.getLocationContext(), CausesPointerEscape: false); |
310 | if (!State) |
311 | return nullptr; |
312 | return setErrnoState(State, EState: MustBeChecked); |
313 | } |
314 | |
315 | } // namespace errno_modeling |
316 | } // namespace ento |
317 | } // namespace clang |
318 | |
319 | void ento::registerErrnoModeling(CheckerManager &mgr) { |
320 | mgr.registerChecker<ErrnoModeling>(); |
321 | } |
322 | |
323 | bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) { |
324 | return true; |
325 | } |
326 | |