File: fuchsia_handle.cpp

package info (click to toggle)
llvm-toolchain-13 1%3A13.0.1-11
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,418,840 kB
  • sloc: cpp: 5,290,826; ansic: 996,570; asm: 544,593; python: 188,212; objc: 72,027; lisp: 30,291; f90: 25,395; sh: 24,898; javascript: 9,780; pascal: 9,398; perl: 7,484; ml: 5,432; awk: 3,523; makefile: 2,913; xml: 953; cs: 573; fortran: 539
file content (508 lines) | stat: -rw-r--r-- 14,463 bytes parent folder | download | duplicates (18)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
// RUN: %clang_analyze_cc1 -analyzer-checker=core,fuchsia.HandleChecker -analyzer-output=text \
// RUN:     -verify %s

typedef __typeof__(sizeof(int)) size_t;
typedef int zx_status_t;
typedef __typeof__(sizeof(int)) zx_handle_t;
typedef unsigned int uint32_t;
#define NULL ((void *)0)
#define ZX_HANDLE_INVALID 0

#if defined(__clang__)
#define ZX_HANDLE_ACQUIRE __attribute__((acquire_handle("Fuchsia")))
#define ZX_HANDLE_RELEASE __attribute__((release_handle("Fuchsia")))
#define ZX_HANDLE_USE __attribute__((use_handle("Fuchsia")))
#define ZX_HANDLE_ACQUIRE_UNOWNED __attribute__((acquire_handle("FuchsiaUnowned")))
#else
#define ZX_HANDLE_ACQUIRE
#define ZX_HANDLE_RELEASE
#define ZX_HANDLE_USE
#define ZX_HANDLE_ACQUIRE_UNOWNED
#endif

zx_status_t zx_channel_create(
    uint32_t options,
    zx_handle_t *out0 ZX_HANDLE_ACQUIRE,
    zx_handle_t *out1 ZX_HANDLE_ACQUIRE);

zx_status_t zx_handle_close(
    zx_handle_t handle ZX_HANDLE_RELEASE);

ZX_HANDLE_ACQUIRE_UNOWNED
zx_handle_t zx_process_self();

void zx_process_self_param(zx_handle_t *out ZX_HANDLE_ACQUIRE_UNOWNED);

ZX_HANDLE_ACQUIRE
zx_handle_t return_handle();

void escape1(zx_handle_t *in);
void escape2(zx_handle_t in);
void (*escape3)(zx_handle_t) = escape2;

void use1(const zx_handle_t *in ZX_HANDLE_USE);
void use2(zx_handle_t in ZX_HANDLE_USE);

void moreArgs(zx_handle_t, int, ...);
void lessArgs(zx_handle_t, int a = 5);

// To test if argument indexes are OK for operator calls.
struct MyType {
  ZX_HANDLE_ACQUIRE
  zx_handle_t operator+(zx_handle_t ZX_HANDLE_RELEASE replace);
};

void checkUnownedHandle01() {
  zx_handle_t h0;
  h0 = zx_process_self(); // expected-note {{Function 'zx_process_self' returns an unowned handle}}
  zx_handle_close(h0);    // expected-warning {{Releasing an unowned handle}}
                          // expected-note@-1 {{Releasing an unowned handle}}
}

void checkUnownedHandle02() {
  zx_handle_t h0;
  zx_process_self_param(&h0); // expected-note {{Unowned handle allocated through 1st parameter}}
  zx_handle_close(h0);        // expected-warning {{Releasing an unowned handle}}
                              // expected-note@-1 {{Releasing an unowned handle}}
}

void checkInvalidHandle01() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb);
  if (sa == ZX_HANDLE_INVALID)
    ;
  // Will we ever see a warning like below?
  // We eagerly replace the symbol with a constant and lose info...
  use2(sa); // TODOexpected-warning {{Use of an invalid handle}}
  zx_handle_close(sb);
  zx_handle_close(sa);
}

void checkInvalidHandle2() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb);
  if (sb != ZX_HANDLE_INVALID)
    zx_handle_close(sb);
  if (sa != ZX_HANDLE_INVALID)
    zx_handle_close(sa);
}

void handleDieBeforeErrorSymbol01() {
  zx_handle_t sa, sb;
  zx_status_t status = zx_channel_create(0, &sa, &sb);
  if (status < 0)
    return;
  __builtin_trap();
}

void handleDieBeforeErrorSymbol02() {
  zx_handle_t sa, sb;
  zx_status_t status = zx_channel_create(0, &sa, &sb);
  // FIXME: There appears to be non-determinism in choosing
  // which handle to report.
  // expected-note-re@-3 {{Handle allocated through {{(2nd|3rd)}} parameter}}
  if (status == 0) { // expected-note {{Assuming 'status' is equal to 0}}
                     // expected-note@-1 {{Taking true branch}}
    return;          // expected-warning {{Potential leak of handle}}
                     // expected-note@-1 {{Potential leak of handle}}
  }
  __builtin_trap();
}

void checkNoCrash01() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb);
  moreArgs(sa, 1, 2, 3, 4, 5);
  lessArgs(sa);
  zx_handle_close(sa);
  zx_handle_close(sb);
}

void checkNoLeak01() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb);
  zx_handle_close(sa);
  zx_handle_close(sb);
}

void checkNoLeak02() {
  zx_handle_t ay[2];
  zx_channel_create(0, &ay[0], &ay[1]);
  zx_handle_close(ay[0]);
  zx_handle_close(ay[1]);
}

void checkNoLeak03() {
  zx_handle_t ay[2];
  zx_channel_create(0, &ay[0], &ay[1]);
  for (int i = 0; i < 2; i++)
    zx_handle_close(ay[i]);
}

zx_handle_t checkNoLeak04() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb);
  zx_handle_close(sa);
  return sb; // no warning
}

zx_handle_t checkNoLeak05(zx_handle_t *out1) {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb);
  *out1 = sa;
  return sb; // no warning
}

void checkNoLeak06() {
  zx_handle_t sa, sb;
  if (zx_channel_create(0, &sa, &sb))
    return;
  zx_handle_close(sa);
  zx_handle_close(sb);
}

void checkLeak01(int tag) {
  zx_handle_t sa, sb;
  if (zx_channel_create(0, &sa, &sb)) // expected-note    {{Handle allocated through 2nd parameter}}
    return;                           // expected-note@-1 {{Assuming the condition is false}}
                                      // expected-note@-2 {{Taking false branch}}
  use1(&sa);
  if (tag) // expected-note {{Assuming 'tag' is 0}}
    zx_handle_close(sa);
  // expected-note@-2 {{Taking false branch}}
  use2(sb); // expected-warning {{Potential leak of handle}}
  // expected-note@-1 {{Potential leak of handle}}
  zx_handle_close(sb);
}

void checkLeakFromReturn01(int tag) {
  zx_handle_t sa = return_handle(); // expected-note {{Function 'return_handle' returns an open handle}}
  (void)sa;
} // expected-note {{Potential leak of handle}}
// expected-warning@-1 {{Potential leak of handle}}

void checkReportLeakOnOnePath(int tag) {
  zx_handle_t sa, sb;
  if (zx_channel_create(0, &sa, &sb)) // expected-note {{Handle allocated through 2nd parameter}}
    return;                           // expected-note@-1 {{Assuming the condition is false}}
                                      // expected-note@-2 {{Taking false branch}}
  zx_handle_close(sb);
  switch (tag) { // expected-note {{Control jumps to the 'default' case at line}}
  case 0:
    use2(sa);
    return;
  case 1:
    use2(sa);
    return;
  case 2:
    use2(sa);
    return;
  case 3:
    use2(sa);
    return;
  case 4:
    use2(sa);
    return;
  default:
    use2(sa);
    return; // expected-warning {{Potential leak of handle}}
            // expected-note@-1 {{Potential leak of handle}}
  }
}

void checkDoubleRelease01(int tag) {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb);
  // expected-note@-1 {{Handle allocated through 2nd parameter}}
  if (tag)               // expected-note {{Assuming 'tag' is not equal to 0}}
    zx_handle_close(sa); // expected-note {{Handle released through 1st parameter}}
  // expected-note@-2 {{Taking true branch}}
  zx_handle_close(sa); // expected-warning {{Releasing a previously released handle}}
  // expected-note@-1 {{Releasing a previously released handle}}
  zx_handle_close(sb);
}

void checkUseAfterFree01(int tag) {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb);
  // expected-note@-1 {{Handle allocated through 2nd parameter}}
  // expected-note@-2 {{Handle allocated through 3rd parameter}}
  // expected-note@+2 {{Taking true branch}}
  // expected-note@+1 {{Taking false branch}}
  if (tag) {
    // expected-note@-1 {{Assuming 'tag' is not equal to 0}}
    zx_handle_close(sa); // expected-note {{Handle released through 1st parameter}}
    use1(&sa);           // expected-warning {{Using a previously released handle}}
    // expected-note@-1 {{Using a previously released handle}}
  }
  // expected-note@-6 {{Assuming 'tag' is 0}}
  zx_handle_close(sb); // expected-note {{Handle released through 1st parameter}}
  use2(sb);            // expected-warning {{Using a previously released handle}}
  // expected-note@-1 {{Using a previously released handle}}
}

void checkMemberOperatorIndices() {
  zx_handle_t sa, sb, sc;
  zx_channel_create(0, &sa, &sb);
  zx_handle_close(sb);
  MyType t;
  sc = t + sa;
  zx_handle_close(sc);
}

struct HandleStruct {
  zx_handle_t h;
};

void close_handle_struct(HandleStruct hs ZX_HANDLE_RELEASE);

void use_handle_struct(HandleStruct hs ZX_HANDLE_USE);

void checkHandleInStructureUseAfterFree() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb); // expected-note {{Handle allocated through 3rd parameter}}
  HandleStruct hs;
  hs.h = sb;
  use_handle_struct(hs);
  close_handle_struct(hs); // expected-note {{Handle released through 1st parameter}}
  zx_handle_close(sa);

  use2(sb); // expected-warning {{Using a previously released handle}}
  // expected-note@-1 {{Using a previously released handle}}
}

void checkHandleInStructureUseAfterFree2() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb); // expected-note {{Handle allocated through 3rd parameter}}
  HandleStruct hs;
  hs.h = sb;
  use_handle_struct(hs);
  zx_handle_close(sb); // expected-note {{Handle released through 1st parameter}}
  zx_handle_close(sa);

  use_handle_struct(hs); // expected-warning {{Using a previously released handle}}
  // expected-note@-1 {{Using a previously released handle}}
}

void checkHandleInStructureLeak() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb); // expected-note {{Handle allocated through 3rd parameter}}
  HandleStruct hs;
  hs.h = sb;
  zx_handle_close(sa); // expected-warning {{Potential leak of handle}}
  // expected-note@-1 {{Potential leak of handle}}
}

struct HandlePtrStruct {
  zx_handle_t *h;
};

void close_handle_struct(HandlePtrStruct hs ZX_HANDLE_RELEASE);

void use_handle_struct(HandlePtrStruct hs ZX_HANDLE_USE);

void checkHandlePtrInStructureUseAfterFree() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb);
  HandlePtrStruct hs;
  hs.h = &sb;
  use_handle_struct(hs);
  close_handle_struct(hs); // expected-note {{Handle released through 1st parameter}}
  zx_handle_close(sa);

  use2(sb); // expected-warning {{Using a previously released handle}}
  // expected-note@-1 {{Using a previously released handle}}
}

void checkHandlePtrInStructureUseAfterFree2() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb);
  HandlePtrStruct hs;
  hs.h = &sb;
  use_handle_struct(hs);
  zx_handle_close(sb); // expected-note {{Handle released through 1st parameter}}
  zx_handle_close(sa);

  use_handle_struct(hs); // expected-warning {{Using a previously released handle}}
  // expected-note@-1 {{Using a previously released handle}}
}

void checkHandlePtrInStructureLeak() {
  zx_handle_t sa, sb;
  zx_channel_create(0, &sa, &sb); // expected-note {{Handle allocated through 3rd parameter}}
  HandlePtrStruct hs;
  hs.h = &sb;
  zx_handle_close(sa); // expected-warning {{Potential leak of handle}}
  // expected-note@-1 {{Potential leak of handle}}
}

// Assume this function's declaration that has the release annotation is in one
// header file while its implementation is in another file. We have to annotate
// the declaration because it might be used outside the TU.
// We also want to make sure it is okay to call the function within the same TU.
zx_status_t test_release_handle(zx_handle_t handle ZX_HANDLE_RELEASE) {
  return zx_handle_close(handle);
}

void checkReleaseImplementedFunc() {
  zx_handle_t a, b;
  zx_channel_create(0, &a, &b);
  zx_handle_close(a);
  test_release_handle(b);
}

void use_handle(zx_handle_t handle) {
  // Do nothing.
}

void test_call_by_value() {
  zx_handle_t a, b;
  zx_channel_create(0, &a, &b);
  zx_handle_close(a);
  use_handle(b);
  zx_handle_close(b);
}

void test_call_by_value_leak() {
  zx_handle_t a, b;
  zx_channel_create(0, &a, &b); // expected-note {{Handle allocated through 3rd parameter}}
  zx_handle_close(a);
  // Here we are passing handle b as integer value to a function that could be
  // analyzed by the analyzer, thus the handle should not be considered escaped.
  // After the function 'use_handle', handle b is still tracked and should be
  // reported leaked.
  use_handle(b);
} // expected-warning {{Potential leak of handle}}
// expected-note@-1 {{Potential leak of handle}}

// RAII

template <typename T>
struct HandleWrapper {
  ~HandleWrapper() { close(); }
  void close() {
    if (handle != ZX_HANDLE_INVALID)
      zx_handle_close(handle);
  }
  T *get_handle_address() { return &handle; }

private:
  T handle;
};

void doNotWarnOnRAII() {
  HandleWrapper<zx_handle_t> w1;
  zx_handle_t sb;
  if (zx_channel_create(0, w1.get_handle_address(), &sb))
    return;
  zx_handle_close(sb);
}

template <typename T>
struct HandleWrapperUnkonwDtor {
  ~HandleWrapperUnkonwDtor();
  void close() {
    if (handle != ZX_HANDLE_INVALID)
      zx_handle_close(handle);
  }
  T *get_handle_address() { return &handle; }

private:
  T handle;
};

void doNotWarnOnUnknownDtor() {
  HandleWrapperUnkonwDtor<zx_handle_t> w1;
  zx_handle_t sb;
  if (zx_channel_create(0, w1.get_handle_address(), &sb))
    return;
  zx_handle_close(sb);
}

// Various escaping scenarios

zx_handle_t *get_handle_address();

void escape_store_to_escaped_region01() {
  zx_handle_t sb;
  if (zx_channel_create(0, get_handle_address(), &sb))
    return;
  zx_handle_close(sb);
}

struct object {
  zx_handle_t *get_handle_address();
};

void escape_store_to_escaped_region02(object &o) {
  zx_handle_t sb;
  // Same as above.
  if (zx_channel_create(0, o.get_handle_address(), &sb))
    return;
  zx_handle_close(sb);
}

void escape_store_to_escaped_region03(object o) {
  zx_handle_t sb;
  // Should we consider the pointee of get_handle_address escaped?
  // Maybe we only should it consider escaped if o escapes?
  if (zx_channel_create(0, o.get_handle_address(), &sb))
    return;
  zx_handle_close(sb);
}

void escape_through_call(int tag) {
  zx_handle_t sa, sb;
  if (zx_channel_create(0, &sa, &sb))
    return;
  escape1(&sa);
  if (tag)
    escape2(sb);
  else
    escape3(sb);
}

struct have_handle {
  zx_handle_t h;
  zx_handle_t *hp;
};

void escape_through_store01(have_handle *handle) {
  zx_handle_t sa;
  if (zx_channel_create(0, &sa, handle->hp))
    return;
  handle->h = sa;
}

have_handle global;
void escape_through_store02() {
  zx_handle_t sa;
  if (zx_channel_create(0, &sa, global.hp))
    return;
  global.h = sa;
}

have_handle escape_through_store03() {
  zx_handle_t sa, sb;
  if (zx_channel_create(0, &sa, &sb))
    return {0, nullptr};
  zx_handle_close(sb);
  return {sa, nullptr};
}

void escape_structs(have_handle *);
void escape_transitively01() {
  zx_handle_t sa, sb;
  if (zx_channel_create(0, &sa, &sb))
    return;
  have_handle hs[2];
  hs[1] = {sa, &sb};
  escape_structs(hs);
}

void escape_top_level_pointees(zx_handle_t *h) {
  zx_handle_t h2;
  if (zx_channel_create(0, h, &h2))
    return;
  zx_handle_close(h2);
} // *h should be escaped here. Right?