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(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 | |
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(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 | |
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(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 | |
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(region: 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(R: ErrnoR, T: 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(location: loc::MemRegionVal{ErrnoR}, V: 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 | 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 | |
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, EState: Irrelevant); |
222 | } |
223 | |
224 | bool isErrnoLocationCall(const CallEvent &CE) { |
225 | return ErrnoLocationCalls.contains(Call: CE); |
226 | } |
227 | |
228 | const 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 | |
239 | ProgramStateRef setErrnoForStdSuccess(ProgramStateRef State, |
240 | CheckerContext &C) { |
241 | return setErrnoState(State, EState: MustNotBeChecked); |
242 | } |
243 | |
244 | ProgramStateRef 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 | |
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(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 | |
274 | void ento::registerErrnoModeling(CheckerManager &mgr) { |
275 | mgr.registerChecker<ErrnoModeling>(); |
276 | } |
277 | |
278 | bool ento::shouldRegisterErrnoModeling(const CheckerManager &mgr) { |
279 | return true; |
280 | } |
281 | |