/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/FuchsiaHandleChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //=== FuchsiaHandleChecker.cpp - Find handle leaks/double closes -*- 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 checker checks if the handle of Fuchsia is properly used according to |
10 | | // following rules. |
11 | | // - If a handle is acquired, it should be released before execution |
12 | | // ends. |
13 | | // - If a handle is released, it should not be released again. |
14 | | // - If a handle is released, it should not be used for other purposes |
15 | | // such as I/O. |
16 | | // |
17 | | // In this checker, each tracked handle is associated with a state. When the |
18 | | // handle variable is passed to different function calls or syscalls, its state |
19 | | // changes. The state changes can be generally represented by following ASCII |
20 | | // Art: |
21 | | // |
22 | | // |
23 | | // +-------------+ +------------+ |
24 | | // acquire_func succeeded | | Escape | | |
25 | | // +-----------------> Allocated +---------> Escaped <--+ |
26 | | // | | | | | | |
27 | | // | +-----+------++ +------------+ | |
28 | | // | | | | |
29 | | // acquire_func | release_func | +--+ | |
30 | | // failed | | | handle +--------+ | |
31 | | // +---------+ | | | dies | | | |
32 | | // | | | +----v-----+ +---------> Leaked | | |
33 | | // | | | | | |(REPORT)| | |
34 | | // | +----------+--+ | Released | Escape +--------+ | |
35 | | // | | | | +---------------------------+ |
36 | | // +--> Not tracked | +----+---+-+ |
37 | | // | | | | As argument by value |
38 | | // +----------+--+ release_func | +------+ in function call |
39 | | // | | | or by reference in |
40 | | // | | | use_func call |
41 | | // unowned | +----v-----+ | +-----------+ |
42 | | // acquire_func | | Double | +-----> Use after | |
43 | | // succeeded | | released | | released | |
44 | | // | | (REPORT) | | (REPORT) | |
45 | | // +---------------+ +----------+ +-----------+ |
46 | | // | Allocated | |
47 | | // | Unowned | release_func |
48 | | // | +---------+ |
49 | | // +---------------+ | |
50 | | // | |
51 | | // +-----v----------+ |
52 | | // | Release of | |
53 | | // | unowned handle | |
54 | | // | (REPORT) | |
55 | | // +----------------+ |
56 | | // |
57 | | // acquire_func represents the functions or syscalls that may acquire a handle. |
58 | | // release_func represents the functions or syscalls that may release a handle. |
59 | | // use_func represents the functions or syscall that requires an open handle. |
60 | | // |
61 | | // If a tracked handle dies in "Released" or "Not Tracked" state, we assume it |
62 | | // is properly used. Otherwise a bug and will be reported. |
63 | | // |
64 | | // Note that, the analyzer does not always know for sure if a function failed |
65 | | // or succeeded. In those cases we use the state MaybeAllocated. |
66 | | // Thus, the diagram above captures the intent, not implementation details. |
67 | | // |
68 | | // Due to the fact that the number of handle related syscalls in Fuchsia |
69 | | // is large, we adopt the annotation attributes to descript syscalls' |
70 | | // operations(acquire/release/use) on handles instead of hardcoding |
71 | | // everything in the checker. |
72 | | // |
73 | | // We use following annotation attributes for handle related syscalls or |
74 | | // functions: |
75 | | // 1. __attribute__((acquire_handle("Fuchsia"))) |handle will be acquired |
76 | | // 2. __attribute__((release_handle("Fuchsia"))) |handle will be released |
77 | | // 3. __attribute__((use_handle("Fuchsia"))) |handle will not transit to |
78 | | // escaped state, it also needs to be open. |
79 | | // |
80 | | // For example, an annotated syscall: |
81 | | // zx_status_t zx_channel_create( |
82 | | // uint32_t options, |
83 | | // zx_handle_t* out0 __attribute__((acquire_handle("Fuchsia"))) , |
84 | | // zx_handle_t* out1 __attribute__((acquire_handle("Fuchsia")))); |
85 | | // denotes a syscall which will acquire two handles and save them to 'out0' and |
86 | | // 'out1' when succeeded. |
87 | | // |
88 | | //===----------------------------------------------------------------------===// |
89 | | |
90 | | #include "clang/AST/Attr.h" |
91 | | #include "clang/AST/Decl.h" |
92 | | #include "clang/AST/Type.h" |
93 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
94 | | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
95 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
96 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
97 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
98 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
99 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" |
100 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" |
101 | | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" |
102 | | #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" |
103 | | #include "llvm/ADT/StringExtras.h" |
104 | | #include <optional> |
105 | | |
106 | | using namespace clang; |
107 | | using namespace ento; |
108 | | |
109 | | namespace { |
110 | | |
111 | | static const StringRef HandleTypeName = "zx_handle_t"; |
112 | | static const StringRef ErrorTypeName = "zx_status_t"; |
113 | | |
114 | | class HandleState { |
115 | | private: |
116 | | enum class Kind { MaybeAllocated, Allocated, Released, Escaped, Unowned } K; |
117 | | SymbolRef ErrorSym; |
118 | 168 | HandleState(Kind K, SymbolRef ErrorSym) : K(K), ErrorSym(ErrorSym) {} |
119 | | |
120 | | public: |
121 | 28 | bool operator==(const HandleState &Other) const { |
122 | 28 | return K == Other.K && ErrorSym == Other.ErrorSym; |
123 | 28 | } |
124 | 102 | bool isAllocated() const { return K == Kind::Allocated; } |
125 | 167 | bool maybeAllocated() const { return K == Kind::MaybeAllocated; } |
126 | 74 | bool isReleased() const { return K == Kind::Released; } |
127 | 111 | bool isEscaped() const { return K == Kind::Escaped; } |
128 | 49 | bool isUnowned() const { return K == Kind::Unowned; } |
129 | | |
130 | 74 | static HandleState getMaybeAllocated(SymbolRef ErrorSym) { |
131 | 74 | return HandleState(Kind::MaybeAllocated, ErrorSym); |
132 | 74 | } |
133 | 24 | static HandleState getAllocated(ProgramStateRef State, HandleState S) { |
134 | 24 | assert(S.maybeAllocated()); |
135 | 24 | assert(State->getConstraintManager() |
136 | 24 | .isNull(State, S.getErrorSym()) |
137 | 24 | .isConstrained()); |
138 | 24 | return HandleState(Kind::Allocated, nullptr); |
139 | 24 | } |
140 | 50 | static HandleState getReleased() { |
141 | 50 | return HandleState(Kind::Released, nullptr); |
142 | 50 | } |
143 | 18 | static HandleState getEscaped() { |
144 | 18 | return HandleState(Kind::Escaped, nullptr); |
145 | 18 | } |
146 | 2 | static HandleState getUnowned() { |
147 | 2 | return HandleState(Kind::Unowned, nullptr); |
148 | 2 | } |
149 | | |
150 | 885 | SymbolRef getErrorSym() const { return ErrorSym; } |
151 | | |
152 | 269 | void Profile(llvm::FoldingSetNodeID &ID) const { |
153 | 269 | ID.AddInteger(static_cast<int>(K)); |
154 | 269 | ID.AddPointer(ErrorSym); |
155 | 269 | } |
156 | | |
157 | 0 | LLVM_DUMP_METHOD void dump(raw_ostream &OS) const { |
158 | 0 | switch (K) { |
159 | 0 | #define CASE(ID) \ |
160 | 0 | case ID: \ |
161 | 0 | OS << #ID; \ |
162 | 0 | break; |
163 | 0 | CASE(Kind::MaybeAllocated) |
164 | 0 | CASE(Kind::Allocated) |
165 | 0 | CASE(Kind::Released) |
166 | 0 | CASE(Kind::Escaped) |
167 | 0 | CASE(Kind::Unowned) |
168 | 0 | } |
169 | 0 | if (ErrorSym) { |
170 | 0 | OS << " ErrorSym: "; |
171 | 0 | ErrorSym->dumpToStream(OS); |
172 | 0 | } |
173 | 0 | } |
174 | | |
175 | 0 | LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } |
176 | | }; |
177 | | |
178 | 993 | template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { |
179 | 993 | return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"361 ; |
180 | 993 | } FuchsiaHandleChecker.cpp:bool (anonymous namespace)::hasFuchsiaAttr<clang::AcquireHandleAttr>(clang::Decl const*) Line | Count | Source | 178 | 374 | template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { | 179 | 374 | return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"149 ; | 180 | 374 | } |
FuchsiaHandleChecker.cpp:bool (anonymous namespace)::hasFuchsiaAttr<clang::ReleaseHandleAttr>(clang::Decl const*) Line | Count | Source | 178 | 450 | template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { | 179 | 450 | return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"165 ; | 180 | 450 | } |
FuchsiaHandleChecker.cpp:bool (anonymous namespace)::hasFuchsiaAttr<clang::UseHandleAttr>(clang::Decl const*) Line | Count | Source | 178 | 169 | template <typename Attr> static bool hasFuchsiaAttr(const Decl *D) { | 179 | 169 | return D->hasAttr<Attr>() && D->getAttr<Attr>()->getHandleType() == "Fuchsia"47 ; | 180 | 169 | } |
|
181 | | |
182 | 152 | template <typename Attr> static bool hasFuchsiaUnownedAttr(const Decl *D) { |
183 | 152 | return D->hasAttr<Attr>() && |
184 | 152 | D->getAttr<Attr>()->getHandleType() == "FuchsiaUnowned"2 ; |
185 | 152 | } |
186 | | |
187 | | class FuchsiaHandleChecker |
188 | | : public Checker<check::PostCall, check::PreCall, check::DeadSymbols, |
189 | | check::PointerEscape, eval::Assume> { |
190 | | BugType LeakBugType{this, "Fuchsia handle leak", "Fuchsia Handle Error", |
191 | | /*SuppressOnSink=*/true}; |
192 | | BugType DoubleReleaseBugType{this, "Fuchsia handle double release", |
193 | | "Fuchsia Handle Error"}; |
194 | | BugType UseAfterReleaseBugType{this, "Fuchsia handle use after release", |
195 | | "Fuchsia Handle Error"}; |
196 | | BugType ReleaseUnownedBugType{ |
197 | | this, "Fuchsia handle release of unowned handle", "Fuchsia Handle Error"}; |
198 | | |
199 | | public: |
200 | | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
201 | | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
202 | | void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; |
203 | | ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, |
204 | | bool Assumption) const; |
205 | | ProgramStateRef checkPointerEscape(ProgramStateRef State, |
206 | | const InvalidatedSymbols &Escaped, |
207 | | const CallEvent *Call, |
208 | | PointerEscapeKind Kind) const; |
209 | | |
210 | | ExplodedNode *reportLeaks(ArrayRef<SymbolRef> LeakedHandles, |
211 | | CheckerContext &C, ExplodedNode *Pred) const; |
212 | | |
213 | | void reportDoubleRelease(SymbolRef HandleSym, const SourceRange &Range, |
214 | | CheckerContext &C) const; |
215 | | |
216 | | void reportUnownedRelease(SymbolRef HandleSym, const SourceRange &Range, |
217 | | CheckerContext &C) const; |
218 | | |
219 | | void reportUseAfterFree(SymbolRef HandleSym, const SourceRange &Range, |
220 | | CheckerContext &C) const; |
221 | | |
222 | | void reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, CheckerContext &C, |
223 | | const SourceRange *Range, const BugType &Type, |
224 | | StringRef Msg) const; |
225 | | |
226 | | void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, |
227 | | const char *Sep) const override; |
228 | | }; |
229 | | } // end anonymous namespace |
230 | | |
231 | | REGISTER_MAP_WITH_PROGRAMSTATE(HStateMap, SymbolRef, HandleState) |
232 | | |
233 | | static const ExplodedNode *getAcquireSite(const ExplodedNode *N, SymbolRef Sym, |
234 | 16 | CheckerContext &Ctx) { |
235 | 16 | ProgramStateRef State = N->getState(); |
236 | | // When bug type is handle leak, exploded node N does not have state info for |
237 | | // leaking handle. Get the predecessor of N instead. |
238 | 16 | if (!State->get<HStateMap>(Sym)) |
239 | 0 | N = N->getFirstPred(); |
240 | | |
241 | 16 | const ExplodedNode *Pred = N; |
242 | 397 | while (N) { |
243 | 397 | State = N->getState(); |
244 | 397 | if (!State->get<HStateMap>(Sym)) { |
245 | 16 | const HandleState *HState = Pred->getState()->get<HStateMap>(Sym); |
246 | 16 | if (HState && (HState->isAllocated() || HState->maybeAllocated())) |
247 | 16 | return N; |
248 | 16 | } |
249 | 381 | Pred = N; |
250 | 381 | N = N->getFirstPred(); |
251 | 381 | } |
252 | 0 | return nullptr; |
253 | 16 | } |
254 | | |
255 | | namespace { |
256 | | class FuchsiaHandleSymbolVisitor final : public SymbolVisitor { |
257 | | public: |
258 | 44 | bool VisitSymbol(SymbolRef S) override { |
259 | 44 | if (const auto *HandleType = S->getType()->getAs<TypedefType>()) |
260 | 42 | if (HandleType->getDecl()->getName() == HandleTypeName) |
261 | 42 | Symbols.push_back(S); |
262 | 44 | return true; |
263 | 44 | } |
264 | | |
265 | 41 | SmallVector<SymbolRef, 1024> GetSymbols() { return Symbols; } |
266 | | |
267 | | private: |
268 | | SmallVector<SymbolRef, 1024> Symbols; |
269 | | }; |
270 | | } // end anonymous namespace |
271 | | |
272 | | /// Returns the symbols extracted from the argument or empty vector if it cannot |
273 | | /// be found. It is unlikely to have over 1024 symbols in one argument. |
274 | | static SmallVector<SymbolRef, 1024> |
275 | 528 | getFuchsiaHandleSymbols(QualType QT, SVal Arg, ProgramStateRef State) { |
276 | 528 | int PtrToHandleLevel = 0; |
277 | 736 | while (QT->isAnyPointerType() || QT->isReferenceType()546 ) { |
278 | 208 | ++PtrToHandleLevel; |
279 | 208 | QT = QT->getPointeeType(); |
280 | 208 | } |
281 | 528 | if (QT->isStructureType()) { |
282 | | // If we see a structure, see if there is any handle referenced by the |
283 | | // structure. |
284 | 41 | FuchsiaHandleSymbolVisitor Visitor; |
285 | 41 | State->scanReachableSymbols(Arg, Visitor); |
286 | 41 | return Visitor.GetSymbols(); |
287 | 41 | } |
288 | 487 | if (const auto *HandleType = QT->getAs<TypedefType>()) { |
289 | 481 | if (HandleType->getDecl()->getName() != HandleTypeName) |
290 | 89 | return {}; |
291 | 392 | if (PtrToHandleLevel > 1) |
292 | | // Not supported yet. |
293 | 0 | return {}; |
294 | | |
295 | 392 | if (PtrToHandleLevel == 0) { |
296 | 205 | SymbolRef Sym = Arg.getAsSymbol(); |
297 | 205 | if (Sym) { |
298 | 199 | return {Sym}; |
299 | 199 | } else { |
300 | 6 | return {}; |
301 | 6 | } |
302 | 205 | } else { |
303 | 187 | assert(PtrToHandleLevel == 1); |
304 | 187 | if (std::optional<Loc> ArgLoc = Arg.getAs<Loc>()) { |
305 | 187 | SymbolRef Sym = State->getSVal(*ArgLoc).getAsSymbol(); |
306 | 187 | if (Sym) { |
307 | 120 | return {Sym}; |
308 | 120 | } else { |
309 | 67 | return {}; |
310 | 67 | } |
311 | 187 | } |
312 | 187 | } |
313 | 392 | } |
314 | 6 | return {}; |
315 | 487 | } |
316 | | |
317 | | void FuchsiaHandleChecker::checkPreCall(const CallEvent &Call, |
318 | 158 | CheckerContext &C) const { |
319 | 158 | ProgramStateRef State = C.getState(); |
320 | 158 | const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); |
321 | 158 | if (!FuncDecl) { |
322 | | // Unknown call, escape by value handles. They are not covered by |
323 | | // PointerEscape callback. |
324 | 2 | for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg1 ) { |
325 | 1 | if (SymbolRef Handle = Call.getArgSVal(Arg).getAsSymbol()) |
326 | 1 | State = State->set<HStateMap>(Handle, HandleState::getEscaped()); |
327 | 1 | } |
328 | 1 | C.addTransition(State); |
329 | 1 | return; |
330 | 1 | } |
331 | | |
332 | 356 | for (unsigned Arg = 0; 157 Arg < Call.getNumArgs(); ++Arg199 ) { |
333 | 206 | if (Arg >= FuncDecl->getNumParams()) |
334 | 1 | break; |
335 | 205 | const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); |
336 | 205 | SmallVector<SymbolRef, 1024> Handles = |
337 | 205 | getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); |
338 | | |
339 | | // Handled in checkPostCall. |
340 | 205 | if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD) || |
341 | 205 | hasFuchsiaAttr<AcquireHandleAttr>(PVD)148 ) |
342 | 129 | continue; |
343 | | |
344 | 76 | for (SymbolRef Handle : Handles) { |
345 | 38 | const HandleState *HState = State->get<HStateMap>(Handle); |
346 | 38 | if (!HState || HState->isEscaped()37 ) |
347 | 2 | continue; |
348 | | |
349 | 36 | if (hasFuchsiaAttr<UseHandleAttr>(PVD) || |
350 | 36 | PVD->getType()->isIntegerType()16 ) { |
351 | 24 | if (HState->isReleased()) { |
352 | 6 | reportUseAfterFree(Handle, Call.getArgSourceRange(Arg), C); |
353 | 6 | return; |
354 | 6 | } |
355 | 24 | } |
356 | 36 | } |
357 | 76 | } |
358 | 151 | C.addTransition(State); |
359 | 151 | } |
360 | | |
361 | | void FuchsiaHandleChecker::checkPostCall(const CallEvent &Call, |
362 | 152 | CheckerContext &C) const { |
363 | 152 | const FunctionDecl *FuncDecl = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); |
364 | 152 | if (!FuncDecl) |
365 | 1 | return; |
366 | | |
367 | | // If we analyzed the function body, then ignore the annotations. |
368 | 151 | if (C.wasInlined) |
369 | 24 | return; |
370 | | |
371 | 127 | ProgramStateRef State = C.getState(); |
372 | | |
373 | 127 | std::vector<std::function<std::string(BugReport & BR)>> Notes; |
374 | 127 | SymbolRef ResultSymbol = nullptr; |
375 | 127 | if (const auto *TypeDefTy = FuncDecl->getReturnType()->getAs<TypedefType>()) |
376 | 91 | if (TypeDefTy->getDecl()->getName() == ErrorTypeName) |
377 | 88 | ResultSymbol = Call.getReturnValue().getAsSymbol(); |
378 | | |
379 | | // Function returns an open handle. |
380 | 127 | if (hasFuchsiaAttr<AcquireHandleAttr>(FuncDecl)) { |
381 | 2 | SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); |
382 | 2 | Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { |
383 | 1 | auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); |
384 | 1 | if (PathBR->getInterestingnessKind(RetSym)) { |
385 | 1 | std::string SBuf; |
386 | 1 | llvm::raw_string_ostream OS(SBuf); |
387 | 1 | OS << "Function '" << FuncDecl->getDeclName() |
388 | 1 | << "' returns an open handle"; |
389 | 1 | return SBuf; |
390 | 1 | } else |
391 | 0 | return ""; |
392 | 1 | }); |
393 | 2 | State = |
394 | 2 | State->set<HStateMap>(RetSym, HandleState::getMaybeAllocated(nullptr)); |
395 | 125 | } else if (hasFuchsiaUnownedAttr<AcquireHandleAttr>(FuncDecl)) { |
396 | | // Function returns an unowned handle |
397 | 1 | SymbolRef RetSym = Call.getReturnValue().getAsSymbol(); |
398 | 1 | Notes.push_back([RetSym, FuncDecl](BugReport &BR) -> std::string { |
399 | 1 | auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); |
400 | 1 | if (PathBR->getInterestingnessKind(RetSym)) { |
401 | 1 | std::string SBuf; |
402 | 1 | llvm::raw_string_ostream OS(SBuf); |
403 | 1 | OS << "Function '" << FuncDecl->getDeclName() |
404 | 1 | << "' returns an unowned handle"; |
405 | 1 | return SBuf; |
406 | 1 | } else |
407 | 0 | return ""; |
408 | 1 | }); |
409 | 1 | State = State->set<HStateMap>(RetSym, HandleState::getUnowned()); |
410 | 1 | } |
411 | | |
412 | 318 | for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg191 ) { |
413 | 195 | if (Arg >= FuncDecl->getNumParams()) |
414 | 1 | break; |
415 | 194 | const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); |
416 | 194 | unsigned ParamDiagIdx = PVD->getFunctionScopeIndex() + 1; |
417 | 194 | SmallVector<SymbolRef, 1024> Handles = |
418 | 194 | getFuchsiaHandleSymbols(PVD->getType(), Call.getArgSVal(Arg), State); |
419 | | |
420 | 194 | for (SymbolRef Handle : Handles) { |
421 | 154 | const HandleState *HState = State->get<HStateMap>(Handle); |
422 | 154 | if (HState && HState->isEscaped()74 ) |
423 | 2 | continue; |
424 | 152 | if (hasFuchsiaAttr<ReleaseHandleAttr>(PVD)) { |
425 | 53 | if (HState && HState->isReleased()50 ) { |
426 | 1 | reportDoubleRelease(Handle, Call.getArgSourceRange(Arg), C); |
427 | 1 | return; |
428 | 52 | } else if (HState && HState->isUnowned()49 ) { |
429 | 2 | reportUnownedRelease(Handle, Call.getArgSourceRange(Arg), C); |
430 | 2 | return; |
431 | 50 | } else { |
432 | 50 | Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { |
433 | 13 | auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); |
434 | 13 | if (PathBR->getInterestingnessKind(Handle)) { |
435 | 7 | std::string SBuf; |
436 | 7 | llvm::raw_string_ostream OS(SBuf); |
437 | 7 | OS << "Handle released through " << ParamDiagIdx |
438 | 7 | << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; |
439 | 7 | return SBuf; |
440 | 7 | } else |
441 | 6 | return ""; |
442 | 13 | }); |
443 | 50 | State = State->set<HStateMap>(Handle, HandleState::getReleased()); |
444 | 50 | } |
445 | 99 | } else if (hasFuchsiaAttr<AcquireHandleAttr>(PVD)) { |
446 | 72 | Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { |
447 | 21 | auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); |
448 | 21 | if (PathBR->getInterestingnessKind(Handle)) { |
449 | 11 | std::string SBuf; |
450 | 11 | llvm::raw_string_ostream OS(SBuf); |
451 | 11 | OS << "Handle allocated through " << ParamDiagIdx |
452 | 11 | << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; |
453 | 11 | return SBuf; |
454 | 11 | } else |
455 | 10 | return ""; |
456 | 21 | }); |
457 | 72 | State = State->set<HStateMap>( |
458 | 72 | Handle, HandleState::getMaybeAllocated(ResultSymbol)); |
459 | 72 | } else if (27 hasFuchsiaUnownedAttr<AcquireHandleAttr>(PVD)27 ) { |
460 | 1 | Notes.push_back([Handle, ParamDiagIdx](BugReport &BR) -> std::string { |
461 | 1 | auto *PathBR = static_cast<PathSensitiveBugReport *>(&BR); |
462 | 1 | if (PathBR->getInterestingnessKind(Handle)) { |
463 | 1 | std::string SBuf; |
464 | 1 | llvm::raw_string_ostream OS(SBuf); |
465 | 1 | OS << "Unowned handle allocated through " << ParamDiagIdx |
466 | 1 | << llvm::getOrdinalSuffix(ParamDiagIdx) << " parameter"; |
467 | 1 | return SBuf; |
468 | 1 | } else |
469 | 0 | return ""; |
470 | 1 | }); |
471 | 1 | State = State->set<HStateMap>(Handle, HandleState::getUnowned()); |
472 | 26 | } else if (!hasFuchsiaAttr<UseHandleAttr>(PVD) && |
473 | 26 | PVD->getType()->isIntegerType()13 ) { |
474 | | // Working around integer by-value escapes. |
475 | | // The by-value escape would not be captured in checkPointerEscape. |
476 | | // If the function was not analyzed (otherwise wasInlined should be |
477 | | // true) and there is no annotation on the handle, we assume the handle |
478 | | // is escaped. |
479 | 2 | State = State->set<HStateMap>(Handle, HandleState::getEscaped()); |
480 | 2 | } |
481 | 152 | } |
482 | 194 | } |
483 | 124 | const NoteTag *T = nullptr; |
484 | 124 | if (!Notes.empty()) { |
485 | 89 | T = C.getNoteTag([this, Notes{std::move(Notes)}]( |
486 | 89 | PathSensitiveBugReport &BR) -> std::string { |
487 | 29 | if (&BR.getBugType() != &UseAfterReleaseBugType && |
488 | 29 | &BR.getBugType() != &LeakBugType13 && |
489 | 29 | &BR.getBugType() != &DoubleReleaseBugType4 && |
490 | 29 | &BR.getBugType() != &ReleaseUnownedBugType2 ) |
491 | 0 | return ""; |
492 | 37 | for (auto &Note : Notes)29 { |
493 | 37 | std::string Text = Note(BR); |
494 | 37 | if (!Text.empty()) |
495 | 21 | return Text; |
496 | 37 | } |
497 | 8 | return ""; |
498 | 29 | }); |
499 | 89 | } |
500 | 124 | C.addTransition(State, T); |
501 | 124 | } |
502 | | |
503 | | void FuchsiaHandleChecker::checkDeadSymbols(SymbolReaper &SymReaper, |
504 | 413 | CheckerContext &C) const { |
505 | 413 | ProgramStateRef State = C.getState(); |
506 | 413 | SmallVector<SymbolRef, 2> LeakedSyms; |
507 | 413 | HStateMapTy TrackedHandles = State->get<HStateMap>(); |
508 | 413 | for (auto &CurItem : TrackedHandles) { |
509 | 386 | SymbolRef ErrorSym = CurItem.second.getErrorSym(); |
510 | | // Keeping zombie handle symbols. In case the error symbol is dying later |
511 | | // than the handle symbol we might produce spurious leak warnings (in case |
512 | | // we find out later from the status code that the handle allocation failed |
513 | | // in the first place). |
514 | 386 | if (!SymReaper.isDead(CurItem.first) || |
515 | 386 | (90 ErrorSym90 && !SymReaper.isDead(ErrorSym)10 )) |
516 | 300 | continue; |
517 | 86 | if (CurItem.second.isAllocated() || CurItem.second.maybeAllocated()77 ) |
518 | 16 | LeakedSyms.push_back(CurItem.first); |
519 | 86 | State = State->remove<HStateMap>(CurItem.first); |
520 | 86 | } |
521 | | |
522 | 413 | ExplodedNode *N = C.getPredecessor(); |
523 | 413 | if (!LeakedSyms.empty()) |
524 | 14 | N = reportLeaks(LeakedSyms, C, N); |
525 | | |
526 | 413 | C.addTransition(State, N); |
527 | 413 | } |
528 | | |
529 | | // Acquiring a handle is not always successful. In Fuchsia most functions |
530 | | // return a status code that determines the status of the handle. |
531 | | // When we split the path based on this status code we know that on one |
532 | | // path we do have the handle and on the other path the acquire failed. |
533 | | // This method helps avoiding false positive leak warnings on paths where |
534 | | // the function failed. |
535 | | // Moreover, when a handle is known to be zero (the invalid handle), |
536 | | // we no longer can follow the symbol on the path, becaue the constant |
537 | | // zero will be used instead of the symbol. We also do not need to release |
538 | | // an invalid handle, so we remove the corresponding symbol from the state. |
539 | | ProgramStateRef FuchsiaHandleChecker::evalAssume(ProgramStateRef State, |
540 | | SVal Cond, |
541 | 367 | bool Assumption) const { |
542 | | // TODO: add notes about successes/fails for APIs. |
543 | 367 | ConstraintManager &Cmr = State->getConstraintManager(); |
544 | 367 | HStateMapTy TrackedHandles = State->get<HStateMap>(); |
545 | 475 | for (auto &CurItem : TrackedHandles) { |
546 | 475 | ConditionTruthVal HandleVal = Cmr.isNull(State, CurItem.first); |
547 | 475 | if (HandleVal.isConstrainedTrue()) { |
548 | | // The handle is invalid. We can no longer follow the symbol on this path. |
549 | 5 | State = State->remove<HStateMap>(CurItem.first); |
550 | 5 | } |
551 | 475 | SymbolRef ErrorSym = CurItem.second.getErrorSym(); |
552 | 475 | if (!ErrorSym) |
553 | 196 | continue; |
554 | 279 | ConditionTruthVal ErrorVal = Cmr.isNull(State, ErrorSym); |
555 | 279 | if (ErrorVal.isConstrainedTrue()) { |
556 | | // Allocation succeeded. |
557 | 24 | if (CurItem.second.maybeAllocated()) |
558 | 24 | State = State->set<HStateMap>( |
559 | 24 | CurItem.first, HandleState::getAllocated(State, CurItem.second)); |
560 | 255 | } else if (ErrorVal.isConstrainedFalse()) { |
561 | | // Allocation failed. |
562 | 26 | if (CurItem.second.maybeAllocated()) |
563 | 26 | State = State->remove<HStateMap>(CurItem.first); |
564 | 26 | } |
565 | 279 | } |
566 | 367 | return State; |
567 | 367 | } |
568 | | |
569 | | ProgramStateRef FuchsiaHandleChecker::checkPointerEscape( |
570 | | ProgramStateRef State, const InvalidatedSymbols &Escaped, |
571 | 106 | const CallEvent *Call, PointerEscapeKind Kind) const { |
572 | 106 | const FunctionDecl *FuncDecl = |
573 | 106 | Call ? dyn_cast_or_null<FunctionDecl>(Call->getDecl())99 : nullptr7 ; |
574 | | |
575 | 106 | llvm::DenseSet<SymbolRef> UnEscaped; |
576 | | // Not all calls should escape our symbols. |
577 | 106 | if (FuncDecl && |
578 | 106 | (98 Kind == PSK_DirectEscapeOnCall98 || Kind == PSK_IndirectEscapeOnCall91 || |
579 | 98 | Kind == PSK_EscapeOutParameters6 )) { |
580 | 227 | for (unsigned Arg = 0; Arg < Call->getNumArgs(); ++Arg129 ) { |
581 | 130 | if (Arg >= FuncDecl->getNumParams()) |
582 | 1 | break; |
583 | 129 | const ParmVarDecl *PVD = FuncDecl->getParamDecl(Arg); |
584 | 129 | SmallVector<SymbolRef, 1024> Handles = |
585 | 129 | getFuchsiaHandleSymbols(PVD->getType(), Call->getArgSVal(Arg), State); |
586 | 129 | for (SymbolRef Handle : Handles) { |
587 | 107 | if (hasFuchsiaAttr<UseHandleAttr>(PVD) || |
588 | 107 | hasFuchsiaAttr<ReleaseHandleAttr>(PVD)93 ) { |
589 | 69 | UnEscaped.insert(Handle); |
590 | 69 | } |
591 | 107 | } |
592 | 129 | } |
593 | 98 | } |
594 | | |
595 | | // For out params, we have to deal with derived symbols. See |
596 | | // MacOSKeychainAPIChecker for details. |
597 | 134 | for (auto I : State->get<HStateMap>()) { |
598 | 134 | if (Escaped.count(I.first) && !UnEscaped.count(I.first)18 ) |
599 | 14 | State = State->set<HStateMap>(I.first, HandleState::getEscaped()); |
600 | 134 | if (const auto *SD = dyn_cast<SymbolDerived>(I.first)) { |
601 | 19 | auto ParentSym = SD->getParentSymbol(); |
602 | 19 | if (Escaped.count(ParentSym)) |
603 | 1 | State = State->set<HStateMap>(I.first, HandleState::getEscaped()); |
604 | 19 | } |
605 | 134 | } |
606 | | |
607 | 106 | return State; |
608 | 106 | } |
609 | | |
610 | | ExplodedNode * |
611 | | FuchsiaHandleChecker::reportLeaks(ArrayRef<SymbolRef> LeakedHandles, |
612 | 14 | CheckerContext &C, ExplodedNode *Pred) const { |
613 | 14 | ExplodedNode *ErrNode = C.generateNonFatalErrorNode(C.getState(), Pred); |
614 | 16 | for (SymbolRef LeakedHandle : LeakedHandles) { |
615 | 16 | reportBug(LeakedHandle, ErrNode, C, nullptr, LeakBugType, |
616 | 16 | "Potential leak of handle"); |
617 | 16 | } |
618 | 14 | return ErrNode; |
619 | 14 | } |
620 | | |
621 | | void FuchsiaHandleChecker::reportDoubleRelease(SymbolRef HandleSym, |
622 | | const SourceRange &Range, |
623 | 1 | CheckerContext &C) const { |
624 | 1 | ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); |
625 | 1 | reportBug(HandleSym, ErrNode, C, &Range, DoubleReleaseBugType, |
626 | 1 | "Releasing a previously released handle"); |
627 | 1 | } |
628 | | |
629 | | void FuchsiaHandleChecker::reportUnownedRelease(SymbolRef HandleSym, |
630 | | const SourceRange &Range, |
631 | 2 | CheckerContext &C) const { |
632 | 2 | ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); |
633 | 2 | reportBug(HandleSym, ErrNode, C, &Range, ReleaseUnownedBugType, |
634 | 2 | "Releasing an unowned handle"); |
635 | 2 | } |
636 | | |
637 | | void FuchsiaHandleChecker::reportUseAfterFree(SymbolRef HandleSym, |
638 | | const SourceRange &Range, |
639 | 6 | CheckerContext &C) const { |
640 | 6 | ExplodedNode *ErrNode = C.generateErrorNode(C.getState()); |
641 | 6 | reportBug(HandleSym, ErrNode, C, &Range, UseAfterReleaseBugType, |
642 | 6 | "Using a previously released handle"); |
643 | 6 | } |
644 | | |
645 | | void FuchsiaHandleChecker::reportBug(SymbolRef Sym, ExplodedNode *ErrorNode, |
646 | | CheckerContext &C, |
647 | | const SourceRange *Range, |
648 | 25 | const BugType &Type, StringRef Msg) const { |
649 | 25 | if (!ErrorNode) |
650 | 0 | return; |
651 | | |
652 | 25 | std::unique_ptr<PathSensitiveBugReport> R; |
653 | 25 | if (Type.isSuppressOnSink()) { |
654 | 16 | const ExplodedNode *AcquireNode = getAcquireSite(ErrorNode, Sym, C); |
655 | 16 | if (AcquireNode) { |
656 | 16 | const Stmt *S = AcquireNode->getStmtForDiagnostics(); |
657 | 16 | assert(S && "Statement cannot be null."); |
658 | 16 | PathDiagnosticLocation LocUsedForUniqueing = |
659 | 16 | PathDiagnosticLocation::createBegin( |
660 | 16 | S, C.getSourceManager(), AcquireNode->getLocationContext()); |
661 | | |
662 | 16 | R = std::make_unique<PathSensitiveBugReport>( |
663 | 16 | Type, Msg, ErrorNode, LocUsedForUniqueing, |
664 | 16 | AcquireNode->getLocationContext()->getDecl()); |
665 | 16 | } |
666 | 16 | } |
667 | 25 | if (!R) |
668 | 9 | R = std::make_unique<PathSensitiveBugReport>(Type, Msg, ErrorNode); |
669 | 25 | if (Range) |
670 | 9 | R->addRange(*Range); |
671 | 25 | R->markInteresting(Sym); |
672 | 25 | C.emitReport(std::move(R)); |
673 | 25 | } |
674 | | |
675 | 1 | void ento::registerFuchsiaHandleChecker(CheckerManager &mgr) { |
676 | 1 | mgr.registerChecker<FuchsiaHandleChecker>(); |
677 | 1 | } |
678 | | |
679 | 2 | bool ento::shouldRegisterFuchsiaHandleChecker(const CheckerManager &mgr) { |
680 | 2 | return true; |
681 | 2 | } |
682 | | |
683 | | void FuchsiaHandleChecker::printState(raw_ostream &Out, ProgramStateRef State, |
684 | 0 | const char *NL, const char *Sep) const { |
685 | |
|
686 | 0 | HStateMapTy StateMap = State->get<HStateMap>(); |
687 | |
|
688 | 0 | if (!StateMap.isEmpty()) { |
689 | 0 | Out << Sep << "FuchsiaHandleChecker :" << NL; |
690 | 0 | for (const auto &[Sym, HandleState] : StateMap) { |
691 | 0 | Sym->dumpToStream(Out); |
692 | 0 | Out << " : "; |
693 | 0 | HandleState.dump(Out); |
694 | 0 | Out << NL; |
695 | 0 | } |
696 | 0 | } |
697 | 0 | } |