]> git.saurik.com Git - apple/libdispatch.git/blob - src/voucher.c
libdispatch-703.1.4.tar.gz
[apple/libdispatch.git] / src / voucher.c
1 /*
2 * Copyright (c) 2013-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_APACHE_LICENSE_HEADER_START@
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * @APPLE_APACHE_LICENSE_HEADER_END@
19 */
20
21 #include "internal.h"
22
23 #if !defined(VOUCHER_EXPORT_PERSONA_SPI)
24 #if TARGET_OS_IPHONE
25 #define VOUCHER_EXPORT_PERSONA_SPI 1
26 #else
27 #define VOUCHER_EXPORT_PERSONA_SPI 0
28 #endif
29 #endif
30
31 #ifndef PERSONA_ID_NONE
32 #define PERSONA_ID_NONE ((uid_t)-1)
33 #endif
34
35 #if !DISPATCH_VARIANT_DYLD_STUB
36
37 #if VOUCHER_USE_MACH_VOUCHER
38 #if !HAVE_PTHREAD_WORKQUEUE_QOS
39 #error Unsupported configuration, workqueue QoS support is required
40 #endif
41 #include <mach/mach_voucher.h>
42 #include <sys/proc_info.h>
43
44 #define MACH_ACTIVITY_ID_RANGE_SIZE 16
45 #define MACH_ACTIVITY_ID_MASK ((1ULL << FIREHOSE_ACTIVITY_ID_FLAGS_SHIFT) - 1)
46 #define FIREHOSE_ACTIVITY_ID_MAKE(aid, flags) \
47 FIREHOSE_ACTIVITY_ID_MERGE_FLAGS((aid) & MACH_ACTIVITY_ID_MASK, flags)
48
49 static volatile uint64_t _voucher_aid_next;
50
51 #pragma mark -
52 #pragma mark voucher_t
53
54 OS_OBJECT_CLASS_DECL(voucher, object);
55 #if !USE_OBJC
56 OS_OBJECT_VTABLE_INSTANCE(voucher,
57 (void (*)(_os_object_t))_voucher_xref_dispose,
58 (void (*)(_os_object_t))_voucher_dispose);
59 #endif // USE_OBJC
60 #define VOUCHER_CLASS OS_OBJECT_VTABLE(voucher)
61
62 static inline voucher_t
63 _voucher_alloc(mach_voucher_attr_recipe_size_t extra)
64 {
65 voucher_t voucher;
66 size_t voucher_size = sizeof(voucher_s) + extra;
67 voucher = (voucher_t)_os_object_alloc_realized(VOUCHER_CLASS, voucher_size);
68 #if VOUCHER_ENABLE_RECIPE_OBJECTS
69 voucher->v_recipe_extra_size = extra;
70 voucher->v_recipe_extra_offset = voucher_size - extra;
71 #else
72 dispatch_assert(!extra);
73 #endif
74 _dispatch_voucher_debug("alloc", voucher);
75 return voucher;
76 }
77
78 #if VOUCHER_ENABLE_RECIPE_OBJECTS
79 voucher_t
80 voucher_create(voucher_recipe_t recipe)
81 {
82 // TODO: capture current activities or current kvoucher ?
83 mach_voucher_attr_recipe_size_t extra = recipe ? recipe->vr_size : 0;
84 voucher_t voucher = _voucher_alloc(extra);
85 if (extra) {
86 memcpy(_voucher_extra_recipes(voucher), recipe->vr_data, extra);
87 }
88 return voucher;
89 }
90 #endif
91
92 DISPATCH_ALWAYS_INLINE
93 static inline voucher_t
94 _voucher_clone(const voucher_t ov, voucher_fields_t ignore_fields)
95 {
96 mach_voucher_attr_recipe_size_t extra = 0;
97 voucher_t v;
98
99 if (ov && !(ignore_fields & VOUCHER_FIELD_EXTRA)) {
100 extra = _voucher_extra_size(ov);
101 }
102 v = _voucher_alloc(extra);
103 if (ov) {
104 voucher_fields_t fields = ~ignore_fields;
105 if ((fields & VOUCHER_FIELD_KVOUCHER) && ov->v_kvoucher) {
106 voucher_t kvb = ov->v_kvbase ? ov->v_kvbase : ov;
107 v->v_kvbase = _voucher_retain(kvb);
108 v->v_kvoucher = kvb->v_kvoucher;
109 v->v_kv_has_importance = kvb->v_kv_has_importance;
110 }
111 if (fields & VOUCHER_FIELD_PRIORITY) {
112 v->v_priority = ov->v_priority;
113 }
114 if (fields & VOUCHER_FIELD_ACTIVITY) {
115 v->v_activity = ov->v_activity;
116 v->v_activity_creator = ov->v_activity_creator;
117 v->v_parent_activity = ov->v_parent_activity;
118 }
119 if ((fields & VOUCHER_FIELD_EXTRA) && extra) {
120 memcpy(_voucher_extra_recipes(v), _voucher_extra_recipes(ov),extra);
121 }
122 }
123 return v;
124 }
125
126 voucher_t
127 voucher_adopt(voucher_t voucher)
128 {
129 if (voucher == VOUCHER_CURRENT) {
130 return _voucher_copy();
131 }
132 return _voucher_adopt(voucher);
133 }
134
135 voucher_t
136 voucher_copy(void)
137 {
138 return _voucher_copy();
139 }
140
141 voucher_t
142 voucher_copy_without_importance(void)
143 {
144 return _voucher_copy_without_importance();
145 }
146
147 voucher_t
148 voucher_retain(voucher_t voucher)
149 {
150 return _voucher_retain(voucher);
151 }
152
153 void
154 voucher_release(voucher_t voucher)
155 {
156 return _voucher_release(voucher);
157 }
158
159 void
160 _voucher_thread_cleanup(void *voucher)
161 {
162 // when a thread exits and has a voucher left, the kernel
163 // will get rid of the voucher kernel object that is set on the thread,
164 // we only need to release the voucher_t object.
165 _voucher_release(voucher);
166 }
167
168 DISPATCH_CACHELINE_ALIGN
169 static TAILQ_HEAD(, voucher_s) _vouchers[VL_HASH_SIZE];
170 #define _vouchers_head(kv) (&_vouchers[VL_HASH((kv))])
171 static dispatch_unfair_lock_s _vouchers_lock;
172 #define _vouchers_lock_lock() _dispatch_unfair_lock_lock(&_vouchers_lock)
173 #define _vouchers_lock_unlock() _dispatch_unfair_lock_unlock(&_vouchers_lock)
174
175 static voucher_t
176 _voucher_find_and_retain(mach_voucher_t kv)
177 {
178 voucher_t v;
179 if (!kv) return NULL;
180 _vouchers_lock_lock();
181 TAILQ_FOREACH(v, _vouchers_head(kv), v_list) {
182 if (v->v_ipc_kvoucher == kv) {
183 int xref_cnt = os_atomic_inc2o(v, os_obj_xref_cnt, relaxed);
184 _dispatch_voucher_debug("retain -> %d", v, xref_cnt + 1);
185 if (slowpath(xref_cnt < 0)) {
186 _dispatch_voucher_debug("over-release", v);
187 _OS_OBJECT_CLIENT_CRASH("Voucher over-release");
188 }
189 if (xref_cnt == 0) {
190 // resurrection: raced with _voucher_remove
191 (void)os_atomic_inc2o(v, os_obj_ref_cnt, relaxed);
192 }
193 break;
194 }
195 }
196 _vouchers_lock_unlock();
197 return v;
198 }
199
200 static void
201 _voucher_insert(voucher_t v)
202 {
203 mach_voucher_t kv = v->v_ipc_kvoucher;
204 if (!kv) return;
205 _vouchers_lock_lock();
206 if (slowpath(_TAILQ_IS_ENQUEUED(v, v_list))) {
207 _dispatch_voucher_debug("corruption", v);
208 DISPATCH_CLIENT_CRASH(v->v_list.tqe_prev, "Voucher corruption");
209 }
210 TAILQ_INSERT_TAIL(_vouchers_head(kv), v, v_list);
211 _vouchers_lock_unlock();
212 }
213
214 static void
215 _voucher_remove(voucher_t v)
216 {
217 mach_voucher_t kv = v->v_ipc_kvoucher;
218 if (!_TAILQ_IS_ENQUEUED(v, v_list)) return;
219 _vouchers_lock_lock();
220 if (slowpath(!kv)) {
221 _dispatch_voucher_debug("corruption", v);
222 DISPATCH_CLIENT_CRASH(0, "Voucher corruption");
223 }
224 // check for resurrection race with _voucher_find_and_retain
225 if (os_atomic_load2o(v, os_obj_xref_cnt, ordered) < 0 &&
226 _TAILQ_IS_ENQUEUED(v, v_list)) {
227 TAILQ_REMOVE(_vouchers_head(kv), v, v_list);
228 _TAILQ_MARK_NOT_ENQUEUED(v, v_list);
229 v->v_list.tqe_next = (void*)~0ull;
230 }
231 _vouchers_lock_unlock();
232 }
233
234 void
235 _voucher_dealloc_mach_voucher(mach_voucher_t kv)
236 {
237 _dispatch_kvoucher_debug("dealloc", kv);
238 _dispatch_voucher_debug_machport(kv);
239 kern_return_t kr = mach_voucher_deallocate(kv);
240 DISPATCH_VERIFY_MIG(kr);
241 (void)dispatch_assume_zero(kr);
242 }
243
244 static inline kern_return_t
245 _voucher_create_mach_voucher(const mach_voucher_attr_recipe_data_t *recipes,
246 size_t recipes_size, mach_voucher_t *kvp)
247 {
248 kern_return_t kr;
249 mach_port_t mhp = _dispatch_get_mach_host_port();
250 mach_voucher_t kv = MACH_VOUCHER_NULL;
251 mach_voucher_attr_raw_recipe_array_t kvr;
252 mach_voucher_attr_recipe_size_t kvr_size;
253 kvr = (mach_voucher_attr_raw_recipe_array_t)recipes;
254 kvr_size = (mach_voucher_attr_recipe_size_t)recipes_size;
255 kr = host_create_mach_voucher(mhp, kvr, kvr_size, &kv);
256 DISPATCH_VERIFY_MIG(kr);
257 if (!kr) {
258 _dispatch_kvoucher_debug("create", kv);
259 _dispatch_voucher_debug_machport(kv);
260 }
261 *kvp = kv;
262 return kr;
263 }
264
265 void
266 _voucher_task_mach_voucher_init(void* ctxt DISPATCH_UNUSED)
267 {
268 kern_return_t kr;
269 mach_voucher_t kv = MACH_VOUCHER_NULL;
270 #if !VOUCHER_USE_EMPTY_MACH_BASE_VOUCHER
271 static const mach_voucher_attr_recipe_data_t task_create_recipe = {
272 .key = MACH_VOUCHER_ATTR_KEY_BANK,
273 .command = MACH_VOUCHER_ATTR_BANK_CREATE,
274 };
275 kr = _voucher_create_mach_voucher(&task_create_recipe,
276 sizeof(task_create_recipe), &kv);
277 if (slowpath(kr)) {
278 DISPATCH_CLIENT_CRASH(kr, "Could not create task mach voucher");
279 }
280 _voucher_default_task_mach_voucher = kv;
281 #endif
282 _voucher_task_mach_voucher = kv;
283 }
284
285 void
286 voucher_replace_default_voucher(void)
287 {
288 (void)_voucher_get_task_mach_voucher(); // initalize task mach voucher
289 mach_voucher_t kv, tkv = MACH_VOUCHER_NULL;
290 voucher_t v = _voucher_get();
291 if (v && v->v_kvoucher) {
292 kern_return_t kr;
293 kv = v->v_ipc_kvoucher ? v->v_ipc_kvoucher : v->v_kvoucher;
294 const mach_voucher_attr_recipe_data_t task_copy_recipe = {
295 .key = MACH_VOUCHER_ATTR_KEY_BANK,
296 .command = MACH_VOUCHER_ATTR_COPY,
297 .previous_voucher = kv,
298 };
299 kr = _voucher_create_mach_voucher(&task_copy_recipe,
300 sizeof(task_copy_recipe), &tkv);
301 if (dispatch_assume_zero(kr)) {
302 tkv = MACH_VOUCHER_NULL;
303 }
304 }
305 if (!tkv) tkv = _voucher_default_task_mach_voucher;
306 kv = os_atomic_xchg(&_voucher_task_mach_voucher, tkv, relaxed);
307 if (kv && kv != _voucher_default_task_mach_voucher) {
308 _voucher_dealloc_mach_voucher(kv);
309 }
310 _dispatch_voucher_debug("kvoucher[0x%08x] replace default voucher", v, tkv);
311 }
312
313 #define _voucher_mach_recipe_size(payload_size) \
314 (sizeof(mach_voucher_attr_recipe_data_t) + (payload_size))
315
316 #if VOUCHER_USE_MACH_VOUCHER_PRIORITY
317 #define _voucher_mach_recipe_alloca(v) ((mach_voucher_attr_recipe_t)alloca(\
318 _voucher_mach_recipe_size(0) + \
319 _voucher_mach_recipe_size(sizeof(ipc_pthread_priority_value_t)) + \
320 _voucher_mach_recipe_size(sizeof(_voucher_mach_udata_s)) + \
321 _voucher_extra_size(v)))
322 #else
323 #define _voucher_mach_recipe_alloca(v) ((mach_voucher_attr_recipe_t)alloca(\
324 _voucher_mach_recipe_size(0) + \
325 _voucher_mach_recipe_size(sizeof(_voucher_mach_udata_s)) + \
326 _voucher_extra_size(v)))
327 #endif
328
329 DISPATCH_ALWAYS_INLINE
330 static inline mach_voucher_attr_recipe_size_t
331 _voucher_mach_recipe_init(mach_voucher_attr_recipe_t mvar_buf, voucher_s *v,
332 mach_voucher_t kvb, pthread_priority_t pp)
333 {
334 mach_voucher_attr_recipe_size_t extra = _voucher_extra_size(v);
335 mach_voucher_attr_recipe_size_t size = 0;
336
337 // normalize to just the QoS class and 0 relative priority
338 pp &= _PTHREAD_PRIORITY_QOS_CLASS_MASK;
339 if (pp) pp |= _PTHREAD_PRIORITY_PRIORITY_MASK;
340
341 *mvar_buf++ = (mach_voucher_attr_recipe_data_t){
342 .key = MACH_VOUCHER_ATTR_KEY_ALL,
343 .command = MACH_VOUCHER_ATTR_COPY,
344 .previous_voucher = kvb,
345 };
346 size += _voucher_mach_recipe_size(0);
347
348 #if VOUCHER_USE_MACH_VOUCHER_PRIORITY
349 if (pp) {
350 ipc_pthread_priority_value_t value = (ipc_pthread_priority_value_t)pp;
351 *mvar_buf++ = (mach_voucher_attr_recipe_data_t){
352 .key = MACH_VOUCHER_ATTR_KEY_PTHPRIORITY,
353 .command = MACH_VOUCHER_ATTR_PTHPRIORITY_CREATE,
354 .content_size = sizeof(value),
355 };
356 mvar_buf = _dispatch_memappend(mvar_buf, &value);
357 size += _voucher_mach_recipe_size(sizeof(value));
358 }
359 #endif // VOUCHER_USE_MACH_VOUCHER_PRIORITY
360
361 if ((v && v->v_activity) || pp) {
362 _voucher_mach_udata_s *udata_buf;
363 unsigned udata_size = 0;
364
365 if (v && v->v_activity) {
366 udata_size = offsetof(_voucher_mach_udata_s, _vmu_after_activity);
367 } else {
368 udata_size = offsetof(_voucher_mach_udata_s, _vmu_after_priority);
369 }
370 *mvar_buf = (mach_voucher_attr_recipe_data_t){
371 .key = MACH_VOUCHER_ATTR_KEY_USER_DATA,
372 .command = MACH_VOUCHER_ATTR_USER_DATA_STORE,
373 .content_size = udata_size,
374 };
375 udata_buf = (_voucher_mach_udata_s *)(mvar_buf->content);
376
377 if (v && v->v_activity) {
378 *udata_buf = (_voucher_mach_udata_s){
379 .vmu_magic = VOUCHER_MAGIC_V3,
380 .vmu_priority = (_voucher_priority_t)pp,
381 .vmu_activity = v->v_activity,
382 .vmu_activity_pid = v->v_activity_creator,
383 .vmu_parent_activity = v->v_parent_activity,
384 };
385 } else {
386 *udata_buf = (_voucher_mach_udata_s){
387 .vmu_magic = VOUCHER_MAGIC_V3,
388 .vmu_priority = (_voucher_priority_t)pp,
389 };
390 }
391
392 mvar_buf = (mach_voucher_attr_recipe_t)(mvar_buf->content + udata_size);
393 size += _voucher_mach_recipe_size(udata_size);
394 }
395
396 if (extra) {
397 memcpy(mvar_buf, _voucher_extra_recipes(v), extra);
398 size += extra;
399 }
400 return size;
401 }
402
403 mach_voucher_t
404 _voucher_get_mach_voucher(voucher_t voucher)
405 {
406 if (!voucher) return MACH_VOUCHER_NULL;
407 if (voucher->v_ipc_kvoucher) return voucher->v_ipc_kvoucher;
408 mach_voucher_t kvb = voucher->v_kvoucher;
409 if (!kvb) kvb = _voucher_get_task_mach_voucher();
410 if (!voucher->v_activity && !voucher->v_priority &&
411 !_voucher_extra_size(voucher)) {
412 return kvb;
413 }
414
415 mach_voucher_attr_recipe_t mvar = _voucher_mach_recipe_alloca(voucher);
416 mach_voucher_attr_recipe_size_t size;
417 mach_voucher_t kv, kvo;
418 kern_return_t kr;
419
420 size = _voucher_mach_recipe_init(mvar, voucher, kvb, voucher->v_priority);
421 kr = _voucher_create_mach_voucher(mvar, size, &kv);
422 if (dispatch_assume_zero(kr) || !kv){
423 return MACH_VOUCHER_NULL;
424 }
425 if (!os_atomic_cmpxchgv2o(voucher, v_ipc_kvoucher, MACH_VOUCHER_NULL,
426 kv, &kvo, relaxed)) {
427 _voucher_dealloc_mach_voucher(kv);
428 kv = kvo;
429 } else {
430 if (kv == voucher->v_kvoucher) {
431 // if v_kvoucher == v_ipc_kvoucher we keep only one reference
432 _voucher_dealloc_mach_voucher(kv);
433 }
434 _voucher_insert(voucher);
435 _dispatch_voucher_debug("kvoucher[0x%08x] create", voucher, kv);
436 }
437 return kv;
438 }
439
440 mach_voucher_t
441 _voucher_create_mach_voucher_with_priority(voucher_t voucher,
442 pthread_priority_t priority)
443 {
444 if (priority == _voucher_get_priority(voucher)) {
445 return MACH_VOUCHER_NULL; // caller will use _voucher_get_mach_voucher
446 }
447 kern_return_t kr;
448 mach_voucher_t kv, kvb = voucher ? voucher->v_kvoucher : MACH_VOUCHER_NULL;
449 if (!kvb) kvb = _voucher_get_task_mach_voucher();
450
451 mach_voucher_attr_recipe_t mvar = _voucher_mach_recipe_alloca(voucher);
452 mach_voucher_attr_recipe_size_t size;
453
454 size = _voucher_mach_recipe_init(mvar, voucher, kvb, priority);
455 kr = _voucher_create_mach_voucher(mvar, size, &kv);
456 if (dispatch_assume_zero(kr) || !kv){
457 return MACH_VOUCHER_NULL;
458 }
459 _dispatch_kvoucher_debug("create with priority from voucher[%p]", kv,
460 voucher);
461 return kv;
462 }
463
464 static voucher_t
465 _voucher_create_with_mach_voucher(mach_voucher_t kv, mach_msg_bits_t msgh_bits)
466 {
467 if (!kv) return NULL;
468 kern_return_t kr;
469 mach_voucher_attr_recipe_t vr;
470 size_t vr_size;
471 mach_voucher_attr_recipe_size_t kvr_size = 0;
472 mach_voucher_attr_content_size_t udata_sz = 0;
473 _voucher_mach_udata_s *udata = NULL;
474 #if !VOUCHER_USE_BANK_AUTOREDEEM
475 mach_voucher_t rkv;
476 const mach_voucher_attr_recipe_data_t redeem_recipe[] = {
477 [0] = {
478 .key = MACH_VOUCHER_ATTR_KEY_ALL,
479 .command = MACH_VOUCHER_ATTR_COPY,
480 .previous_voucher = kv,
481 },
482 [1] = {
483 .key = MACH_VOUCHER_ATTR_KEY_BANK,
484 .command = MACH_VOUCHER_ATTR_REDEEM,
485 },
486 };
487 kr = _voucher_create_mach_voucher(redeem_recipe, sizeof(redeem_recipe),
488 &rkv);
489 if (!dispatch_assume_zero(kr)) {
490 _voucher_dealloc_mach_voucher(kv);
491 _dispatch_kvoucher_debug("redeemed from 0x%08x", rkv, kv);
492 kv = rkv;
493 } else {
494 _dispatch_voucher_debug_machport(kv);
495 }
496 #endif
497 voucher_t v = _voucher_find_and_retain(kv);
498 if (v) {
499 _dispatch_voucher_debug("kvoucher[0x%08x] found", v, kv);
500 _voucher_dealloc_mach_voucher(kv);
501 return v;
502 }
503 vr_size = sizeof(*vr) + sizeof(_voucher_mach_udata_s);
504 vr = alloca(vr_size);
505 if (kv) {
506 kvr_size = (mach_voucher_attr_recipe_size_t)vr_size;
507 kr = mach_voucher_extract_attr_recipe(kv,
508 MACH_VOUCHER_ATTR_KEY_USER_DATA, (void*)vr, &kvr_size);
509 DISPATCH_VERIFY_MIG(kr);
510 if (!dispatch_assume_zero(kr) && kvr_size >= sizeof(*vr)) {
511 udata_sz = vr->content_size;
512 udata = (_voucher_mach_udata_s*)vr->content;
513 dispatch_assume(udata_sz >= sizeof(_voucher_magic_t));
514 }
515 }
516 vr = NULL;
517
518 v = _voucher_alloc(0);
519 v->v_ipc_kvoucher = v->v_kvoucher = kv;
520 v->v_kv_has_importance = !!(msgh_bits & MACH_MSGH_BITS_RAISEIMP);
521
522 if (udata_sz >= offsetof(_voucher_mach_udata_s,_vmu_after_priority)){
523 if (udata->vmu_magic == VOUCHER_MAGIC_V3) {
524 v->v_priority = udata->vmu_priority;
525 }
526 }
527 bool remove_kv_userdata = false;
528 if (udata_sz >= offsetof(_voucher_mach_udata_s, _vmu_after_activity)) {
529 #if !RDAR_25050791
530 remove_kv_userdata = true;
531 #endif
532 if (udata->vmu_magic == VOUCHER_MAGIC_V3 && udata->vmu_activity) {
533 v->v_activity = udata->vmu_activity;
534 v->v_activity_creator = udata->vmu_activity_pid;
535 v->v_parent_activity = udata->vmu_parent_activity;
536 }
537 }
538
539 if (remove_kv_userdata) {
540 mach_voucher_t nkv = MACH_VOUCHER_NULL;
541 const mach_voucher_attr_recipe_data_t remove_userdata_recipe[] = {
542 [0] = {
543 .key = MACH_VOUCHER_ATTR_KEY_ALL,
544 .command = MACH_VOUCHER_ATTR_COPY,
545 .previous_voucher = kv,
546 },
547 [1] = {
548 .key = MACH_VOUCHER_ATTR_KEY_USER_DATA,
549 .command = MACH_VOUCHER_ATTR_REMOVE,
550 },
551 #if VOUCHER_USE_MACH_VOUCHER_PRIORITY
552 [2] = {
553 .key = MACH_VOUCHER_ATTR_KEY_PTHPRIORITY,
554 .command = MACH_VOUCHER_ATTR_REMOVE,
555 },
556 #endif
557 };
558 mach_voucher_attr_recipe_size_t size = sizeof(remove_userdata_recipe);
559
560 kr = _voucher_create_mach_voucher(remove_userdata_recipe, size, &nkv);
561 if (!dispatch_assume_zero(kr)) {
562 _dispatch_voucher_debug("kvoucher[0x%08x] udata removal "
563 "(created 0x%08x)", v, kv, nkv);
564 v->v_ipc_kvoucher = MACH_VOUCHER_NULL;
565 v->v_kvoucher = nkv;
566 v->v_kvbase = _voucher_find_and_retain(nkv);
567 if (v->v_kvbase) {
568 _voucher_dealloc_mach_voucher(nkv); // borrow base reference
569 }
570 _voucher_dealloc_mach_voucher(kv);
571 kv = nkv;
572 } else {
573 _dispatch_voucher_debug_machport(kv);
574 }
575 }
576
577 _voucher_insert(v);
578 _dispatch_voucher_debug("kvoucher[0x%08x] create", v, kv);
579 return v;
580 }
581
582 voucher_t
583 _voucher_create_with_priority_and_mach_voucher(voucher_t ov,
584 pthread_priority_t priority, mach_voucher_t kv)
585 {
586 if (priority == _voucher_get_priority(ov)) {
587 if (kv) _voucher_dealloc_mach_voucher(kv);
588 return ov ? _voucher_retain(ov) : NULL;
589 }
590 voucher_t v = _voucher_find_and_retain(kv);
591 voucher_fields_t ignore_fields = VOUCHER_FIELD_PRIORITY;
592
593 if (v) {
594 _dispatch_voucher_debug("kvoucher[0x%08x] find", v, kv);
595 _voucher_dealloc_mach_voucher(kv);
596 return v;
597 }
598
599 if (kv) ignore_fields |= VOUCHER_FIELD_KVOUCHER;
600 v = _voucher_clone(ov, ignore_fields);
601 if (priority) {
602 v->v_priority = (_voucher_priority_t)priority;
603 }
604 if (kv) {
605 v->v_ipc_kvoucher = v->v_kvoucher = kv;
606 _voucher_insert(v);
607 _dispatch_voucher_debug("kvoucher[0x%08x] create with priority from "
608 "voucher[%p]", v, kv, ov);
609 _dispatch_voucher_debug_machport(kv);
610 }
611 return v;
612 }
613
614 voucher_t
615 _voucher_create_without_importance(voucher_t ov)
616 {
617 // Nothing to do unless the old voucher has a kernel voucher. If it
618 // doesn't, it can't have any importance, now or in the future.
619 if (!ov) return NULL;
620 if (!ov->v_kvoucher || !ov->v_kv_has_importance) return _voucher_retain(ov);
621 kern_return_t kr;
622 mach_voucher_t kv, okv;
623 // Copy kernel voucher, removing importance.
624 okv = ov->v_ipc_kvoucher ? ov->v_ipc_kvoucher : ov->v_kvoucher;
625 const mach_voucher_attr_recipe_data_t importance_remove_recipe[] = {
626 [0] = {
627 .key = MACH_VOUCHER_ATTR_KEY_ALL,
628 .command = MACH_VOUCHER_ATTR_COPY,
629 .previous_voucher = okv,
630 },
631 [1] = {
632 .key = MACH_VOUCHER_ATTR_KEY_IMPORTANCE,
633 .command = MACH_VOUCHER_ATTR_REMOVE,
634 },
635 };
636 kr = _voucher_create_mach_voucher(importance_remove_recipe,
637 sizeof(importance_remove_recipe), &kv);
638 if (dispatch_assume_zero(kr) || !kv){
639 if (ov->v_ipc_kvoucher) return NULL;
640 kv = MACH_VOUCHER_NULL;
641 }
642 if (kv == okv) {
643 _voucher_dealloc_mach_voucher(kv);
644 return _voucher_retain(ov);
645 }
646 voucher_t v = _voucher_find_and_retain(kv);
647 if (v && ov->v_ipc_kvoucher) {
648 _dispatch_voucher_debug("kvoucher[0x%08x] find without importance "
649 "from voucher[%p]", v, kv, ov);
650 _voucher_dealloc_mach_voucher(kv);
651 return v;
652 }
653 voucher_t kvbase = v;
654 voucher_fields_t ignore_fields = VOUCHER_FIELD_KVOUCHER;
655 v = _voucher_clone(ov, ignore_fields);
656 v->v_kvoucher = kv;
657 if (ov->v_ipc_kvoucher) {
658 v->v_ipc_kvoucher = kv;
659 _voucher_insert(v);
660 } else if (kvbase) {
661 v->v_kvbase = kvbase;
662 _voucher_dealloc_mach_voucher(kv); // borrow base reference
663 }
664 if (!kvbase) {
665 _dispatch_voucher_debug("kvoucher[0x%08x] create without importance "
666 "from voucher[%p]", v, kv, ov);
667 }
668 return v;
669 }
670
671 voucher_t
672 _voucher_create_accounting_voucher(voucher_t ov)
673 {
674 // Nothing to do unless the old voucher has a kernel voucher. If it does
675 // doesn't, it can't have any accounting attributes.
676 if (!ov || !ov->v_kvoucher) return NULL;
677 kern_return_t kr = KERN_SUCCESS;
678 mach_voucher_t okv, kv = MACH_VOUCHER_NULL;
679 okv = ov->v_ipc_kvoucher ? ov->v_ipc_kvoucher : ov->v_kvoucher;
680 const mach_voucher_attr_recipe_data_t accounting_copy_recipe = {
681 .key = MACH_VOUCHER_ATTR_KEY_BANK,
682 .command = MACH_VOUCHER_ATTR_COPY,
683 .previous_voucher = okv,
684 };
685 kr = _voucher_create_mach_voucher(&accounting_copy_recipe,
686 sizeof(accounting_copy_recipe), &kv);
687 if (dispatch_assume_zero(kr) || !kv){
688 return NULL;
689 }
690 voucher_t v = _voucher_find_and_retain(kv);
691 if (v) {
692 _dispatch_voucher_debug("kvoucher[0x%08x] find accounting voucher "
693 "from voucher[%p]", v, kv, ov);
694 _voucher_dealloc_mach_voucher(kv);
695 return v;
696 }
697 v = _voucher_alloc(0);
698 v->v_ipc_kvoucher = v->v_kvoucher = kv;
699 if (kv == okv) {
700 v->v_kvbase = _voucher_retain(ov);
701 _voucher_dealloc_mach_voucher(kv); // borrow base reference
702 }
703 _voucher_insert(v);
704 _dispatch_voucher_debug("kvoucher[0x%08x] create accounting voucher "
705 "from voucher[%p]", v, kv, ov);
706 return v;
707 }
708
709 voucher_t
710 voucher_create_with_mach_msg(mach_msg_header_t *msg)
711 {
712 mach_msg_bits_t msgh_bits;
713 mach_voucher_t kv = _voucher_mach_msg_get(msg, &msgh_bits);
714 return _voucher_create_with_mach_voucher(kv, msgh_bits);
715 }
716
717 void
718 voucher_decrement_importance_count4CF(voucher_t v)
719 {
720 if (!v || !v->v_kvoucher || !v->v_kv_has_importance) return;
721 kern_return_t kr;
722 mach_voucher_t kv = v->v_ipc_kvoucher ? v->v_ipc_kvoucher : v->v_kvoucher;
723 uint32_t dec = 1;
724 mach_voucher_attr_content_t kvc_in = (mach_voucher_attr_content_t)&dec;
725 mach_voucher_attr_content_size_t kvc_in_size = sizeof(dec);
726 mach_voucher_attr_content_t kvc_out = NULL;
727 mach_voucher_attr_content_size_t kvc_out_size = 0;
728 #if DISPATCH_DEBUG
729 uint32_t count = UINT32_MAX;
730 kvc_out = (mach_voucher_attr_content_t)&count;
731 kvc_out_size = sizeof(count);
732 #endif
733 kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_IMPORTANCE,
734 MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL, kvc_in, kvc_in_size,
735 kvc_out, &kvc_out_size);
736 DISPATCH_VERIFY_MIG(kr);
737 if (kr == KERN_INVALID_TASK) return; // non-denap receiver rdar://25643185
738 #if DISPATCH_DEBUG
739 _dispatch_voucher_debug("kvoucher[0x%08x] decrement importance count to %u:"
740 " %s - 0x%x", v, kv, count, mach_error_string(kr), kr);
741 #endif
742 if (slowpath(dispatch_assume_zero(kr) == KERN_FAILURE)) {
743 DISPATCH_CLIENT_CRASH(kr, "Voucher importance count underflow");
744 }
745 }
746
747 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
748 mach_voucher_t
749 voucher_get_mach_voucher(voucher_t voucher)
750 {
751 return _voucher_get_mach_voucher(voucher);
752 }
753 #endif
754
755 void
756 _voucher_xref_dispose(voucher_t voucher)
757 {
758 _dispatch_voucher_debug("xref_dispose", voucher);
759 _voucher_remove(voucher);
760 return _os_object_release_internal_inline((_os_object_t)voucher);
761 }
762
763 void
764 _voucher_dispose(voucher_t voucher)
765 {
766 _dispatch_voucher_debug("dispose", voucher);
767 if (slowpath(_TAILQ_IS_ENQUEUED(voucher, v_list))) {
768 _dispatch_voucher_debug("corruption", voucher);
769 DISPATCH_CLIENT_CRASH(voucher->v_list.tqe_prev, "Voucher corruption");
770 }
771 voucher->v_list.tqe_next = DISPATCH_OBJECT_LISTLESS;
772 if (voucher->v_ipc_kvoucher) {
773 if (voucher->v_ipc_kvoucher != voucher->v_kvoucher) {
774 _voucher_dealloc_mach_voucher(voucher->v_ipc_kvoucher);
775 }
776 voucher->v_ipc_kvoucher = MACH_VOUCHER_NULL;
777 }
778 if (voucher->v_kvoucher) {
779 if (!voucher->v_kvbase) {
780 _voucher_dealloc_mach_voucher(voucher->v_kvoucher);
781 }
782 voucher->v_kvoucher = MACH_VOUCHER_NULL;
783 }
784 if (voucher->v_kvbase) {
785 _voucher_release(voucher->v_kvbase);
786 voucher->v_kvbase = NULL;
787 }
788 voucher->v_activity = 0;
789 voucher->v_activity_creator = 0;
790 voucher->v_parent_activity = 0;
791 voucher->v_priority = 0;
792 #if VOUCHER_ENABLE_RECIPE_OBJECTS
793 voucher->v_recipe_extra_size = 0;
794 voucher->v_recipe_extra_offset = 0;
795 #endif
796 return _os_object_dealloc((_os_object_t)voucher);
797 }
798
799 static void
800 _voucher_activity_debug_channel_barrier_nop(void *ctxt DISPATCH_UNUSED)
801 {
802 }
803
804 void
805 _voucher_activity_debug_channel_init(void)
806 {
807 dispatch_mach_handler_function_t handler = NULL;
808
809 if (_voucher_libtrace_hooks && _voucher_libtrace_hooks->vah_version >= 2) {
810 handler = _voucher_libtrace_hooks->vah_debug_channel_handler;
811 }
812
813 if (!handler) return;
814
815 dispatch_mach_t dm;
816 mach_port_t dbgp;
817 kern_return_t kr;
818
819 kr = task_get_debug_control_port(mach_task_self(), &dbgp);
820 DISPATCH_VERIFY_MIG(kr);
821 if (kr) {
822 DISPATCH_CLIENT_CRASH(kr, "Couldn't get debug control port");
823 }
824 if (dbgp) {
825 dm = dispatch_mach_create_f("com.apple.debug-channel",
826 DISPATCH_TARGET_QUEUE_DEFAULT, NULL, handler);
827 dispatch_mach_connect(dm, dbgp, MACH_PORT_NULL, NULL);
828 // will force the DISPATCH_MACH_CONNECTED event
829 dispatch_mach_send_barrier_f(dm, NULL,
830 _voucher_activity_debug_channel_barrier_nop);
831 _voucher_activity_debug_channel = dm;
832 }
833 }
834
835 void
836 _voucher_atfork_child(void)
837 {
838 _dispatch_thread_setspecific(dispatch_voucher_key, NULL);
839 _voucher_task_mach_voucher_pred = 0;
840 _voucher_task_mach_voucher = MACH_VOUCHER_NULL;
841 #if !VOUCHER_USE_EMPTY_MACH_BASE_VOUCHER
842 _voucher_default_task_mach_voucher = MACH_PORT_NULL;
843 #endif
844 _voucher_aid_next = 0;
845 _firehose_task_buffer_pred = 0;
846 _firehose_task_buffer = NULL; // firehose buffer is VM_INHERIT_NONE
847 }
848
849 #if VOUCHER_EXPORT_PERSONA_SPI
850 #if VOUCHER_USE_PERSONA
851 static kern_return_t
852 _voucher_get_current_persona_token(struct persona_token *token)
853 {
854 kern_return_t kr = KERN_FAILURE;
855 voucher_t v = _voucher_get();
856
857 if (v && v->v_kvoucher) {
858 mach_voucher_t kv = v->v_ipc_kvoucher ?: v->v_kvoucher;
859 mach_voucher_attr_content_t kvc_in = NULL;
860 mach_voucher_attr_content_size_t kvc_in_size = 0;
861 mach_voucher_attr_content_t kvc_out =
862 (mach_voucher_attr_content_t)token;
863 mach_voucher_attr_content_size_t kvc_out_size = sizeof(*token);
864
865 kr = mach_voucher_attr_command(kv, MACH_VOUCHER_ATTR_KEY_BANK,
866 BANK_PERSONA_TOKEN, kvc_in, kvc_in_size,
867 kvc_out, &kvc_out_size);
868 if (kr != KERN_NOT_SUPPORTED
869 // Voucher doesn't have a PERSONA_TOKEN
870 && kr != KERN_INVALID_VALUE
871 // Kernel doesn't understand BANK_PERSONA_TOKEN
872 && kr != KERN_INVALID_ARGUMENT) {
873 (void)dispatch_assume_zero(kr);
874 }
875 }
876 return kr;
877 }
878 #endif
879
880 uid_t
881 voucher_get_current_persona(void)
882 {
883 uid_t persona_id = PERSONA_ID_NONE;
884
885 #if VOUCHER_USE_PERSONA
886 struct persona_token token;
887 int err;
888
889 if (_voucher_get_current_persona_token(&token) == KERN_SUCCESS) {
890 return token.originator.persona_id;
891 }
892
893 // falling back to the process persona if there is no adopted voucher
894 if (kpersona_get(&persona_id) < 0) {
895 err = errno;
896 if (err != ESRCH) {
897 (void)dispatch_assume_zero(err);
898 }
899 }
900 #endif
901 return persona_id;
902 }
903
904 int
905 voucher_get_current_persona_originator_info(struct proc_persona_info *persona_info)
906 {
907 #if VOUCHER_USE_PERSONA
908 struct persona_token token;
909 if (_voucher_get_current_persona_token(&token) == KERN_SUCCESS) {
910 *persona_info = token.originator;
911 return 0;
912 }
913 #else
914 (void)persona_info;
915 #endif
916 return -1;
917 }
918
919 int
920 voucher_get_current_persona_proximate_info(struct proc_persona_info *persona_info)
921 {
922 #if VOUCHER_USE_PERSONA
923 struct persona_token token;
924 if (_voucher_get_current_persona_token(&token) == KERN_SUCCESS) {
925 *persona_info = token.proximate;
926 return 0;
927 }
928 #else
929 (void)persona_info;
930 #endif
931 return -1;
932 }
933 #endif
934
935 #pragma mark -
936 #pragma mark _voucher_init
937
938 boolean_t
939 voucher_mach_msg_set(mach_msg_header_t *msg)
940 {
941 return _voucher_mach_msg_set(msg, _voucher_get());
942 }
943
944 void
945 voucher_mach_msg_clear(mach_msg_header_t *msg)
946 {
947 (void)_voucher_mach_msg_clear(msg, false);
948 }
949
950 voucher_mach_msg_state_t
951 voucher_mach_msg_adopt(mach_msg_header_t *msg)
952 {
953 mach_msg_bits_t msgh_bits;
954 mach_voucher_t kv = _voucher_mach_msg_get(msg, &msgh_bits);
955 if (!kv) return VOUCHER_MACH_MSG_STATE_UNCHANGED;
956 voucher_t v = _voucher_create_with_mach_voucher(kv, msgh_bits);
957 return (voucher_mach_msg_state_t)_voucher_adopt(v);
958 }
959
960 void
961 voucher_mach_msg_revert(voucher_mach_msg_state_t state)
962 {
963 if (state == VOUCHER_MACH_MSG_STATE_UNCHANGED) return;
964 _voucher_replace((voucher_t)state);
965 }
966
967 #if DISPATCH_USE_LIBKERNEL_VOUCHER_INIT
968 #include <_libkernel_init.h>
969
970 static const struct _libkernel_voucher_functions _voucher_libkernel_functions =
971 {
972 .version = 1,
973 .voucher_mach_msg_set = voucher_mach_msg_set,
974 .voucher_mach_msg_clear = voucher_mach_msg_clear,
975 .voucher_mach_msg_adopt = voucher_mach_msg_adopt,
976 .voucher_mach_msg_revert = voucher_mach_msg_revert,
977 };
978
979 static void
980 _voucher_libkernel_init(void)
981 {
982 kern_return_t kr = __libkernel_voucher_init(&_voucher_libkernel_functions);
983 dispatch_assert(!kr);
984 }
985 #else
986 #define _voucher_libkernel_init()
987 #endif
988
989 void
990 voucher_activity_initialize_4libtrace(voucher_activity_hooks_t hooks)
991 {
992 if (!os_atomic_cmpxchg(&_voucher_libtrace_hooks, NULL,
993 hooks, relaxed)) {
994 DISPATCH_CLIENT_CRASH(_voucher_libtrace_hooks,
995 "voucher_activity_initialize_4libtrace called twice");
996 }
997 }
998
999 void
1000 _voucher_init(void)
1001 {
1002 _voucher_libkernel_init();
1003 unsigned int i;
1004 for (i = 0; i < VL_HASH_SIZE; i++) {
1005 TAILQ_INIT(&_vouchers[i]);
1006 }
1007 }
1008
1009 #pragma mark -
1010 #pragma mark voucher_activity_t
1011
1012 DISPATCH_NOINLINE
1013 static uint64_t
1014 _voucher_activity_id_allocate_slow(uint64_t aid)
1015 {
1016 kern_return_t kr;
1017 uint64_t next;
1018
1019 kr = mach_generate_activity_id(mach_task_self(), 1, &next);
1020 if (unlikely(kr)) {
1021 DISPATCH_CLIENT_CRASH(kr, "Could not generate an activity ID");
1022 }
1023 next *= MACH_ACTIVITY_ID_RANGE_SIZE;
1024 next &= MACH_ACTIVITY_ID_MASK;
1025 if (unlikely(next == 0)) {
1026 next++;
1027 }
1028
1029 if (unlikely(aid == 0)) {
1030 if (os_atomic_cmpxchg(&_voucher_aid_next, 0, next + 1, relaxed)) {
1031 return next;
1032 }
1033 }
1034 return os_atomic_xchg(&_voucher_aid_next, next, relaxed);
1035 }
1036
1037 DISPATCH_ALWAYS_INLINE
1038 static firehose_activity_id_t
1039 _voucher_activity_id_allocate(firehose_activity_flags_t flags)
1040 {
1041 uint64_t aid, next;
1042 os_atomic_rmw_loop(&_voucher_aid_next, aid, next, relaxed, {
1043 next = aid + 1;
1044 if (aid == 0 || next % MACH_ACTIVITY_ID_RANGE_SIZE == 0) {
1045 os_atomic_rmw_loop_give_up({
1046 aid = _voucher_activity_id_allocate_slow(aid);
1047 break;
1048 });
1049 }
1050 });
1051 return FIREHOSE_ACTIVITY_ID_MAKE(aid, flags);
1052 }
1053
1054 #define _voucher_activity_tracepoint_reserve(stamp, stream, pub, priv, privbuf) \
1055 firehose_buffer_tracepoint_reserve(_firehose_task_buffer, stamp, \
1056 stream, pub, priv, privbuf)
1057
1058 #define _voucher_activity_tracepoint_flush(ft, ftid) \
1059 firehose_buffer_tracepoint_flush(_firehose_task_buffer, ft, ftid)
1060
1061 DISPATCH_NOINLINE
1062 static void
1063 _firehose_task_buffer_init(void *ctx OS_UNUSED)
1064 {
1065 mach_port_t logd_port;
1066
1067 /* Query the uniquepid of the current process */
1068 struct proc_uniqidentifierinfo p_uniqinfo = { };
1069 int info_size = 0;
1070
1071 info_size = proc_pidinfo(getpid(), PROC_PIDUNIQIDENTIFIERINFO, 1,
1072 &p_uniqinfo, PROC_PIDUNIQIDENTIFIERINFO_SIZE);
1073 if (slowpath(info_size != PROC_PIDUNIQIDENTIFIERINFO_SIZE)) {
1074 DISPATCH_INTERNAL_CRASH(info_size, "Unable to get the unique pid");
1075 }
1076 _voucher_unique_pid = p_uniqinfo.p_uniqueid;
1077
1078
1079 if (!fastpath(_voucher_libtrace_hooks)) {
1080 if (0) { // <rdar://problem/23393959>
1081 DISPATCH_CLIENT_CRASH(0,
1082 "Activity subsystem isn't initialized yet");
1083 }
1084 return;
1085 }
1086 logd_port = _voucher_libtrace_hooks->vah_get_logd_port();
1087 if (logd_port) {
1088 unsigned long flags = 0;
1089 #if DISPATCH_USE_MEMORYPRESSURE_SOURCE
1090 if (_dispatch_memory_warn) {
1091 flags |= FIREHOSE_BUFFER_BANK_FLAG_LOW_MEMORY;
1092 }
1093 #endif
1094 // firehose_buffer_create always consumes the send-right
1095 _firehose_task_buffer = firehose_buffer_create(logd_port,
1096 _voucher_unique_pid, flags);
1097 }
1098 }
1099
1100 DISPATCH_ALWAYS_INLINE
1101 static inline bool
1102 _voucher_activity_disabled(void)
1103 {
1104 dispatch_once_f(&_firehose_task_buffer_pred,
1105 NULL, _firehose_task_buffer_init);
1106
1107 firehose_buffer_t fb = _firehose_task_buffer;
1108 if (fastpath(fb)) {
1109 return slowpath(fb->fb_header.fbh_sendp == MACH_PORT_DEAD);
1110 }
1111 return true;
1112 }
1113
1114 void*
1115 voucher_activity_get_metadata_buffer(size_t *length)
1116 {
1117 if (_voucher_activity_disabled()) {
1118 *length = 0;
1119 return NULL;
1120 }
1121
1122 firehose_buffer_header_t fbh = &_firehose_task_buffer->fb_header;
1123
1124 *length = FIREHOSE_BUFFER_LIBTRACE_HEADER_SIZE;
1125 return (void *)((uintptr_t)(fbh + 1) - *length);
1126 }
1127
1128 voucher_t
1129 voucher_activity_create(firehose_tracepoint_id_t trace_id,
1130 voucher_t base, firehose_activity_flags_t flags, uint64_t location)
1131 {
1132 return voucher_activity_create_with_location(&trace_id, base, flags, location);
1133 }
1134
1135 voucher_t
1136 voucher_activity_create_with_location(firehose_tracepoint_id_t *trace_id,
1137 voucher_t base, firehose_activity_flags_t flags, uint64_t location)
1138 {
1139 firehose_activity_id_t va_id = 0, current_id = 0, parent_id = 0;
1140 firehose_tracepoint_id_u ftid = { .ftid_value = *trace_id };
1141 uint16_t pubsize = sizeof(va_id) + sizeof(location);
1142 uint64_t creator_id = 0;
1143 voucher_t ov = _voucher_get();
1144 voucher_t v;
1145
1146 if (base == VOUCHER_CURRENT) {
1147 base = ov;
1148 }
1149 if (_voucher_activity_disabled()) {
1150 *trace_id = 0;
1151 return base ? _voucher_retain(base) : VOUCHER_NULL;
1152 }
1153
1154 FIREHOSE_TRACE_ID_CLEAR_FLAG(ftid, base, has_unique_pid);
1155 if (ov && (current_id = ov->v_activity)) {
1156 FIREHOSE_TRACE_ID_SET_FLAG(ftid, base, has_current_aid);
1157 pubsize += sizeof(firehose_activity_id_t);
1158 if ((creator_id = ov->v_activity_creator)) {
1159 FIREHOSE_TRACE_ID_SET_FLAG(ftid, base, has_unique_pid);
1160 pubsize += sizeof(uint64_t);
1161 }
1162 }
1163 if (base != VOUCHER_NULL) {
1164 parent_id = base->v_activity;
1165 }
1166
1167 if (parent_id) {
1168 FIREHOSE_TRACE_ID_SET_FLAG(ftid, activity, has_other_aid);
1169 pubsize += sizeof(firehose_activity_id_t);
1170 flags |= FIREHOSE_ACTIVITY_ID_FLAGS(parent_id);
1171 }
1172
1173 if (firehose_precise_timestamps_enabled()) {
1174 flags |= firehose_activity_flags_precise_timestamp;
1175 }
1176 voucher_fields_t ignore_fields = VOUCHER_FIELD_ACTIVITY;
1177 v = _voucher_clone(base, ignore_fields);
1178 v->v_activity = va_id = _voucher_activity_id_allocate(flags);
1179 v->v_activity_creator = _voucher_unique_pid;
1180 v->v_parent_activity = parent_id;
1181
1182 static const firehose_stream_t streams[2] = {
1183 firehose_stream_metadata,
1184 firehose_stream_persist,
1185 };
1186 firehose_tracepoint_t ft;
1187 uint64_t stamp = firehose_tracepoint_time(flags);
1188
1189 for (size_t i = 0; i < countof(streams); i++) {
1190 ft = _voucher_activity_tracepoint_reserve(stamp, streams[i], pubsize,
1191 0, NULL);
1192 if (!fastpath(ft)) continue;
1193
1194 uint8_t *pubptr = ft->ft_data;
1195 if (current_id) {
1196 pubptr = _dispatch_memappend(pubptr, &current_id);
1197 }
1198 if (creator_id) {
1199 pubptr = _dispatch_memappend(pubptr, &creator_id);
1200 }
1201 if (parent_id) {
1202 pubptr = _dispatch_memappend(pubptr, &parent_id);
1203 }
1204 pubptr = _dispatch_memappend(pubptr, &va_id);
1205 pubptr = _dispatch_memappend(pubptr, &location);
1206 _voucher_activity_tracepoint_flush(ft, ftid);
1207 }
1208 *trace_id = ftid.ftid_value;
1209 return v;
1210 }
1211
1212 void
1213 _voucher_activity_swap(firehose_activity_id_t old_id,
1214 firehose_activity_id_t new_id)
1215 {
1216 if (_voucher_activity_disabled()) return;
1217
1218 firehose_tracepoint_id_u ftid = { .ftid = {
1219 ._namespace = firehose_tracepoint_namespace_activity,
1220 ._type = _firehose_tracepoint_type_activity_swap,
1221 } };
1222 uint16_t pubsize = 0;
1223
1224 if (old_id) {
1225 FIREHOSE_TRACE_ID_SET_FLAG(ftid, base, has_current_aid);
1226 pubsize += sizeof(firehose_activity_id_t);
1227 }
1228 if (new_id) {
1229 FIREHOSE_TRACE_ID_SET_FLAG(ftid, activity, has_other_aid);
1230 pubsize += sizeof(firehose_activity_id_t);
1231 }
1232
1233 firehose_stream_t stream = firehose_stream_metadata;
1234 firehose_tracepoint_t ft;
1235 firehose_activity_flags_t flags = FIREHOSE_ACTIVITY_ID_FLAGS(old_id) |
1236 FIREHOSE_ACTIVITY_ID_FLAGS(new_id);
1237 uint64_t stamp = firehose_tracepoint_time(flags);
1238
1239 _dispatch_voucher_ktrace_activity_adopt(new_id);
1240
1241 ft = _voucher_activity_tracepoint_reserve(stamp, stream, pubsize, 0, NULL);
1242 if (!fastpath(ft)) return;
1243 uint8_t *pubptr = ft->ft_data;
1244 if (old_id) pubptr = _dispatch_memappend(pubptr, &old_id);
1245 if (new_id) pubptr = _dispatch_memappend(pubptr, &new_id);
1246 _voucher_activity_tracepoint_flush(ft, ftid);
1247 }
1248
1249 firehose_activity_id_t
1250 voucher_get_activity_id_and_creator(voucher_t v, uint64_t *creator_pid,
1251 firehose_activity_id_t *parent_id)
1252 {
1253 if (v == VOUCHER_CURRENT) {
1254 v = _voucher_get();
1255 }
1256 if (v == VOUCHER_NULL) {
1257 if (creator_pid) *creator_pid = 0;
1258 if (parent_id) *parent_id = FIREHOSE_ACTIVITY_ID_NULL;
1259 return FIREHOSE_ACTIVITY_ID_NULL;
1260 }
1261 if (creator_pid) *creator_pid = v->v_activity_creator;
1262 if (parent_id) *parent_id = v->v_parent_activity;
1263 return v->v_activity;
1264 }
1265
1266 firehose_activity_id_t
1267 voucher_get_activity_id(voucher_t v, firehose_activity_id_t *parent_id)
1268 {
1269 return voucher_get_activity_id_and_creator(v, NULL, parent_id);
1270 }
1271
1272 void
1273 voucher_activity_flush(firehose_stream_t stream)
1274 {
1275 if (_voucher_activity_disabled()) return;
1276 firehose_buffer_stream_flush(_firehose_task_buffer, stream);
1277 }
1278
1279 DISPATCH_ALWAYS_INLINE
1280 static inline firehose_tracepoint_id_t
1281 _voucher_activity_trace(firehose_stream_t stream,
1282 firehose_tracepoint_id_u ftid, uint64_t stamp,
1283 const void *pubdata, size_t publen,
1284 const void *privdata, size_t privlen)
1285 {
1286 const uint16_t ft_size = offsetof(struct firehose_tracepoint_s, ft_data);
1287 const size_t _firehose_chunk_payload_size =
1288 sizeof(((struct firehose_buffer_chunk_s *)0)->fbc_data);
1289
1290 if (_voucher_activity_disabled()) return 0;
1291
1292 firehose_tracepoint_t ft;
1293 firehose_activity_id_t va_id = 0;
1294 firehose_buffer_chunk_t fbc;
1295 uint8_t *privptr, *pubptr;
1296 size_t pubsize = publen;
1297 voucher_t ov = _voucher_get();
1298 uint64_t creator_pid;
1299
1300 if ((va_id = _voucher_get_activity_id(ov, &creator_pid))) {
1301 FIREHOSE_TRACE_ID_SET_FLAG(ftid, base, has_current_aid);
1302 pubsize += sizeof(va_id);
1303 }
1304 if (FIREHOSE_TRACE_ID_HAS_FLAG(ftid, base, has_unique_pid)) {
1305 if (creator_pid) {
1306 pubsize += sizeof(creator_pid);
1307 } else {
1308 FIREHOSE_TRACE_ID_CLEAR_FLAG(ftid, base, has_unique_pid);
1309 }
1310 } else {
1311 creator_pid = 0;
1312 }
1313
1314 if (privlen) {
1315 FIREHOSE_TRACE_ID_SET_FLAG(ftid, log, has_private_data);
1316 pubsize += sizeof(struct firehose_buffer_range_s);
1317 }
1318
1319 if (slowpath(ft_size + pubsize + privlen > _firehose_chunk_payload_size)) {
1320 DISPATCH_CLIENT_CRASH(ft_size + pubsize + privlen, "Log is too large");
1321 }
1322
1323 ft = _voucher_activity_tracepoint_reserve(stamp, stream, (uint16_t)pubsize,
1324 (uint16_t)privlen, &privptr);
1325 if (!fastpath(ft)) return 0;
1326 pubptr = ft->ft_data;
1327 if (va_id) {
1328 pubptr = _dispatch_memappend(pubptr, &va_id);
1329 }
1330 if (creator_pid) {
1331 pubptr = _dispatch_memappend(pubptr, &creator_pid);
1332 }
1333 if (privlen) {
1334 fbc = firehose_buffer_chunk_for_address(ft);
1335 struct firehose_buffer_range_s range = {
1336 .fbr_offset = (uint16_t)(privptr - fbc->fbc_start),
1337 .fbr_length = (uint16_t)privlen,
1338 };
1339 pubptr = _dispatch_memappend(pubptr, &range);
1340 _dispatch_mempcpy(privptr, privdata, privlen);
1341 }
1342 _dispatch_mempcpy(pubptr, pubdata, publen);
1343 _voucher_activity_tracepoint_flush(ft, ftid);
1344 return ftid.ftid_value;
1345 }
1346
1347 firehose_tracepoint_id_t
1348 voucher_activity_trace(firehose_stream_t stream,
1349 firehose_tracepoint_id_t trace_id, uint64_t timestamp,
1350 const void *pubdata, size_t publen)
1351 {
1352 firehose_tracepoint_id_u ftid = { .ftid_value = trace_id };
1353 return _voucher_activity_trace(stream, ftid, timestamp, pubdata, publen,
1354 NULL, 0);
1355 }
1356
1357 firehose_tracepoint_id_t
1358 voucher_activity_trace_with_private_strings(firehose_stream_t stream,
1359 firehose_tracepoint_id_t trace_id, uint64_t timestamp,
1360 const void *pubdata, size_t publen,
1361 const void *privdata, size_t privlen)
1362 {
1363 firehose_tracepoint_id_u ftid = { .ftid_value = trace_id };
1364 return _voucher_activity_trace(stream, ftid, timestamp,
1365 pubdata, publen, privdata, privlen);
1366 }
1367
1368 #pragma mark -
1369 #pragma mark _voucher_debug
1370
1371 size_t
1372 _voucher_debug(voucher_t v, char* buf, size_t bufsiz)
1373 {
1374 size_t offset = 0;
1375 #define bufprintf(...) \
1376 offset += dsnprintf(&buf[offset], bufsiz - offset, ##__VA_ARGS__)
1377 bufprintf("voucher[%p] = { xrefcnt = 0x%x, refcnt = 0x%x", v,
1378 v->os_obj_xref_cnt + 1, v->os_obj_ref_cnt + 1);
1379
1380 if (v->v_kvbase) {
1381 bufprintf(", base voucher %p", v->v_kvbase);
1382 }
1383 if (v->v_kvoucher) {
1384 bufprintf(", kvoucher%s 0x%x", v->v_kvoucher == v->v_ipc_kvoucher ?
1385 " & ipc kvoucher" : "", v->v_kvoucher);
1386 }
1387 if (v->v_ipc_kvoucher && v->v_ipc_kvoucher != v->v_kvoucher) {
1388 bufprintf(", ipc kvoucher 0x%x", v->v_ipc_kvoucher);
1389 }
1390 if (v->v_priority) {
1391 bufprintf(", QOS 0x%x", v->v_priority);
1392 }
1393 if (v->v_activity) {
1394 bufprintf(", activity 0x%llx (pid: 0x%16llx, parent 0x%llx)",
1395 v->v_activity, v->v_activity_creator, v->v_parent_activity);
1396 }
1397 bufprintf(" }");
1398 return offset;
1399 }
1400
1401 #else // VOUCHER_USE_MACH_VOUCHER
1402
1403 #pragma mark -
1404 #pragma mark Simulator / vouchers disabled
1405
1406 #if VOUCHER_ENABLE_RECIPE_OBJECTS
1407 voucher_t
1408 voucher_create(voucher_recipe_t recipe)
1409 {
1410 (void)recipe;
1411 return NULL;
1412 }
1413 #endif
1414
1415 voucher_t
1416 voucher_adopt(voucher_t voucher)
1417 {
1418 return voucher;
1419 }
1420
1421 voucher_t
1422 voucher_copy(void)
1423 {
1424 return NULL;
1425 }
1426
1427 voucher_t
1428 voucher_copy_without_importance(void)
1429 {
1430 return NULL;
1431 }
1432
1433 voucher_t
1434 voucher_retain(voucher_t voucher)
1435 {
1436 return voucher;
1437 }
1438
1439 void
1440 voucher_release(voucher_t voucher)
1441 {
1442 (void)voucher;
1443 }
1444
1445 void
1446 voucher_replace_default_voucher(void)
1447 {
1448 }
1449
1450 void
1451 voucher_decrement_importance_count4CF(voucher_t v)
1452 {
1453 (void)v;
1454 }
1455
1456 void
1457 _voucher_thread_cleanup(void *voucher)
1458 {
1459 (void)voucher;
1460 }
1461
1462 void
1463 _voucher_dealloc_mach_voucher(mach_voucher_t kv)
1464 {
1465 (void)kv;
1466 }
1467
1468 mach_voucher_t
1469 _voucher_get_mach_voucher(voucher_t voucher)
1470 {
1471 (void)voucher;
1472 return MACH_VOUCHER_NULL;
1473 }
1474
1475 mach_voucher_t
1476 _voucher_create_mach_voucher_with_priority(voucher_t voucher,
1477 pthread_priority_t priority)
1478 {
1479 (void)voucher; (void)priority;
1480 return MACH_VOUCHER_NULL;
1481 }
1482
1483 voucher_t
1484 _voucher_create_with_priority_and_mach_voucher(voucher_t voucher,
1485 pthread_priority_t priority, mach_voucher_t kv)
1486 {
1487 (void)voucher; (void)priority; (void)kv;
1488 return NULL;
1489 }
1490
1491 voucher_t
1492 _voucher_create_accounting_voucher(voucher_t voucher)
1493 {
1494 (void)voucher;
1495 return NULL;
1496 }
1497
1498 voucher_t
1499 voucher_create_with_mach_msg(mach_msg_header_t *msg)
1500 {
1501 (void)msg;
1502 return NULL;
1503 }
1504
1505 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
1506 mach_voucher_t
1507 voucher_get_mach_voucher(voucher_t voucher)
1508 {
1509 (void)voucher;
1510 return 0;
1511 }
1512 #endif
1513
1514 void
1515 _voucher_xref_dispose(voucher_t voucher)
1516 {
1517 (void)voucher;
1518 }
1519
1520 void
1521 _voucher_dispose(voucher_t voucher)
1522 {
1523 (void)voucher;
1524 }
1525
1526 #if VOUCHER_EXPORT_PERSONA_SPI
1527 uid_t
1528 voucher_get_current_persona(void)
1529 {
1530 return PERSONA_ID_NONE;
1531 }
1532
1533 int
1534 voucher_get_current_persona_originator_info(struct proc_persona_info *persona_info)
1535 {
1536 (void)persona_info;
1537 return -1;
1538 }
1539
1540 int
1541 voucher_get_current_persona_proximate_info(struct proc_persona_info *persona_info)
1542 {
1543 (void)persona_info;
1544 return -1;
1545 }
1546 #endif
1547
1548 void
1549 _voucher_activity_debug_channel_init(void)
1550 {
1551 }
1552
1553 void
1554 _voucher_atfork_child(void)
1555 {
1556 }
1557
1558 void
1559 _voucher_init(void)
1560 {
1561 }
1562
1563 void*
1564 voucher_activity_get_metadata_buffer(size_t *length)
1565 {
1566 *length = 0;
1567 return NULL;
1568 }
1569
1570 voucher_t
1571 voucher_activity_create(firehose_tracepoint_id_t trace_id,
1572 voucher_t base, firehose_activity_flags_t flags, uint64_t location)
1573 {
1574 (void)trace_id; (void)base; (void)flags; (void)location;
1575 return NULL;
1576 }
1577
1578 voucher_t
1579 voucher_activity_create_with_location(firehose_tracepoint_id_t *trace_id,
1580 voucher_t base, firehose_activity_flags_t flags, uint64_t location)
1581 {
1582 (void)trace_id; (void)base; (void)flags; (void)location;
1583 return NULL;
1584 }
1585
1586 firehose_activity_id_t
1587 voucher_get_activity_id(voucher_t voucher, firehose_activity_id_t *parent_id)
1588 {
1589 (void)voucher; (void)parent_id;
1590 return 0;
1591 }
1592
1593 firehose_activity_id_t
1594 voucher_get_activity_id_and_creator(voucher_t voucher, uint64_t *creator_pid,
1595 firehose_activity_id_t *parent_id)
1596 {
1597 if (creator_pid) *creator_pid = 0;
1598 (void)voucher; (void)parent_id;
1599 return 0;
1600 }
1601
1602 firehose_tracepoint_id_t
1603 voucher_activity_trace(firehose_stream_t stream,
1604 firehose_tracepoint_id_t trace_id, uint64_t timestamp,
1605 const void *pubdata, size_t publen)
1606 {
1607 (void)stream; (void)trace_id; (void)timestamp;
1608 (void)pubdata; (void)publen;
1609 return 0;
1610 }
1611
1612 firehose_tracepoint_id_t
1613 voucher_activity_trace_with_private_strings(firehose_stream_t stream,
1614 firehose_tracepoint_id_t trace_id, uint64_t timestamp,
1615 const void *pubdata, size_t publen,
1616 const void *privdata, size_t privlen)
1617 {
1618 (void)stream; (void)trace_id; (void)timestamp;
1619 (void)pubdata; (void)publen; (void)privdata; (void)privlen;
1620 return 0;
1621 }
1622
1623 void
1624 voucher_activity_flush(firehose_stream_t stream)
1625 {
1626 (void)stream;
1627 }
1628
1629 void
1630 voucher_activity_initialize_4libtrace(voucher_activity_hooks_t hooks)
1631 {
1632 (void)hooks;
1633 }
1634
1635 size_t
1636 _voucher_debug(voucher_t v, char* buf, size_t bufsiz)
1637 {
1638 (void)v; (void)buf; (void)bufsiz;
1639 return 0;
1640 }
1641
1642 #endif // VOUCHER_USE_MACH_VOUCHER
1643
1644 #else // DISPATCH_VARIANT_DYLD_STUB
1645
1646 firehose_activity_id_t
1647 voucher_get_activity_id_4dyld(void)
1648 {
1649 #if VOUCHER_USE_MACH_VOUCHER
1650 return _voucher_get_activity_id(_voucher_get(), NULL);
1651 #else
1652 return 0;
1653 #endif
1654 }
1655
1656 #endif // DISPATCH_VARIANT_DYLD_STUB