Coverage Report

Created: 2022-01-18 06:27

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