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