2 * Copyright (c) 2013-2016 Apple Inc. All rights reserved.
4 * @APPLE_APACHE_LICENSE_HEADER_START@
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * @APPLE_APACHE_LICENSE_HEADER_END@
23 #if !defined(VOUCHER_EXPORT_PERSONA_SPI)
25 #define VOUCHER_EXPORT_PERSONA_SPI 1
27 #define VOUCHER_EXPORT_PERSONA_SPI 0
31 #ifndef PERSONA_ID_NONE
32 #define PERSONA_ID_NONE ((uid_t)-1)
35 #if !DISPATCH_VARIANT_DYLD_STUB
37 #if VOUCHER_USE_MACH_VOUCHER
38 #if !HAVE_PTHREAD_WORKQUEUE_QOS
39 #error Unsupported configuration, workqueue QoS support is required
41 #include <mach/mach_voucher.h>
42 #include <sys/proc_info.h>
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)
49 static volatile uint64_t _voucher_aid_next
;
52 #pragma mark voucher_t
54 OS_OBJECT_CLASS_DECL(voucher
, object
);
56 OS_OBJECT_VTABLE_INSTANCE(voucher
,
57 (void (*)(_os_object_t
))_voucher_xref_dispose
,
58 (void (*)(_os_object_t
))_voucher_dispose
);
60 #define VOUCHER_CLASS OS_OBJECT_VTABLE(voucher)
62 static inline voucher_t
63 _voucher_alloc(mach_voucher_attr_recipe_size_t extra
)
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
;
72 dispatch_assert(!extra
);
74 _dispatch_voucher_debug("alloc", voucher
);
78 #if VOUCHER_ENABLE_RECIPE_OBJECTS
80 voucher_create(voucher_recipe_t recipe
)
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
);
86 memcpy(_voucher_extra_recipes(voucher
), recipe
->vr_data
, extra
);
88 _voucher_trace(CREATE
, voucher
, MACH_PORT_NULL
, 0);
93 DISPATCH_ALWAYS_INLINE
94 static inline voucher_t
95 _voucher_clone(const voucher_t ov
, voucher_fields_t ignore_fields
)
97 mach_voucher_attr_recipe_size_t extra
= 0;
100 if (ov
&& !(ignore_fields
& VOUCHER_FIELD_EXTRA
)) {
101 extra
= _voucher_extra_size(ov
);
103 v
= _voucher_alloc(extra
);
105 voucher_fields_t fields
= ~ignore_fields
;
106 if ((fields
& VOUCHER_FIELD_KVOUCHER
) && ov
->v_kvoucher
) {
107 voucher_t kvb
= ov
->v_kvbase
? ov
->v_kvbase
: ov
;
108 v
->v_kvbase
= _voucher_retain(kvb
);
109 v
->v_kvoucher
= kvb
->v_kvoucher
;
110 v
->v_kv_has_importance
= kvb
->v_kv_has_importance
;
112 if (fields
& VOUCHER_FIELD_PRIORITY
) {
113 v
->v_priority
= ov
->v_priority
;
115 if (fields
& VOUCHER_FIELD_ACTIVITY
) {
116 v
->v_activity
= ov
->v_activity
;
117 v
->v_activity_creator
= ov
->v_activity_creator
;
118 v
->v_parent_activity
= ov
->v_parent_activity
;
120 if ((fields
& VOUCHER_FIELD_EXTRA
) && extra
) {
121 memcpy(_voucher_extra_recipes(v
), _voucher_extra_recipes(ov
),extra
);
128 voucher_adopt(voucher_t voucher
)
130 if (voucher
== VOUCHER_CURRENT
) {
131 return _voucher_copy();
133 return _voucher_adopt(voucher
);
139 return _voucher_copy();
143 voucher_copy_without_importance(void)
145 return _voucher_copy_without_importance();
149 voucher_retain(voucher_t voucher
)
151 return _voucher_retain(voucher
);
155 voucher_release(voucher_t voucher
)
157 return _voucher_release(voucher
);
161 _voucher_thread_cleanup(void *voucher
)
163 // when a thread exits and has a voucher left, the kernel
164 // will get rid of the voucher kernel object that is set on the thread,
165 // we only need to release the voucher_t object.
166 _voucher_release(voucher
);
170 #pragma mark voucher_hash
172 DISPATCH_CACHELINE_ALIGN
173 static voucher_hash_head_s _voucher_hash
[VL_HASH_SIZE
];
175 #define _voucher_hash_head(kv) (&_voucher_hash[VL_HASH((kv))])
176 static dispatch_unfair_lock_s _voucher_hash_lock
;
177 #define _voucher_hash_lock_lock() \
178 _dispatch_unfair_lock_lock(&_voucher_hash_lock)
179 #define _voucher_hash_lock_unlock() \
180 _dispatch_unfair_lock_unlock(&_voucher_hash_lock)
182 DISPATCH_ALWAYS_INLINE
184 _voucher_hash_head_init(voucher_hash_head_s
*head
)
186 _voucher_hash_set_next(&head
->vhh_first
, VOUCHER_NULL
);
187 _voucher_hash_set_prev_ptr(&head
->vhh_last_ptr
, &head
->vhh_first
);
190 DISPATCH_ALWAYS_INLINE
192 _voucher_hash_enqueue(mach_voucher_t kv
, voucher_t v
)
194 // same as TAILQ_INSERT_TAIL
195 voucher_hash_head_s
*head
= _voucher_hash_head(kv
);
196 uintptr_t prev_ptr
= head
->vhh_last_ptr
;
197 _voucher_hash_set_next(&v
->v_list
.vhe_next
, VOUCHER_NULL
);
198 v
->v_list
.vhe_prev_ptr
= prev_ptr
;
199 _voucher_hash_store_to_prev_ptr(prev_ptr
, v
);
200 _voucher_hash_set_prev_ptr(&head
->vhh_last_ptr
, &v
->v_list
.vhe_next
);
203 DISPATCH_ALWAYS_INLINE
205 _voucher_hash_remove(mach_voucher_t kv
, voucher_t v
)
207 // same as TAILQ_REMOVE
208 voucher_hash_head_s
*head
= _voucher_hash_head(kv
);
209 voucher_t next
= _voucher_hash_get_next(v
->v_list
.vhe_next
);
210 uintptr_t prev_ptr
= v
->v_list
.vhe_prev_ptr
;
212 next
->v_list
.vhe_prev_ptr
= prev_ptr
;
214 head
->vhh_last_ptr
= prev_ptr
;
216 _voucher_hash_store_to_prev_ptr(prev_ptr
, next
);
217 _voucher_hash_mark_not_enqueued(v
);
221 _voucher_find_and_retain(mach_voucher_t kv
)
223 if (!kv
) return NULL
;
224 _voucher_hash_lock_lock();
225 voucher_hash_head_s
*head
= _voucher_hash_head(kv
);
226 voucher_t v
= _voucher_hash_get_next(head
->vhh_first
);
228 if (v
->v_ipc_kvoucher
== kv
) {
229 int xref_cnt
= os_atomic_inc2o(v
, os_obj_xref_cnt
, relaxed
);
230 _dispatch_voucher_debug("retain -> %d", v
, xref_cnt
+ 1);
231 if (unlikely(xref_cnt
< 0)) {
232 _dispatch_voucher_debug("over-release", v
);
233 _OS_OBJECT_CLIENT_CRASH("Voucher over-release");
236 // resurrection: raced with _voucher_remove
237 (void)os_atomic_inc2o(v
, os_obj_ref_cnt
, relaxed
);
241 v
= _voucher_hash_get_next(v
->v_list
.vhe_next
);
243 _voucher_hash_lock_unlock();
248 _voucher_insert(voucher_t v
)
250 mach_voucher_t kv
= v
->v_ipc_kvoucher
;
252 _voucher_hash_lock_lock();
253 if (unlikely(_voucher_hash_is_enqueued(v
))) {
254 _dispatch_voucher_debug("corruption", v
);
255 DISPATCH_CLIENT_CRASH(0, "Voucher corruption");
257 _voucher_hash_enqueue(kv
, v
);
258 _voucher_hash_lock_unlock();
262 _voucher_remove(voucher_t v
)
264 mach_voucher_t kv
= v
->v_ipc_kvoucher
;
265 if (!_voucher_hash_is_enqueued(v
)) return;
266 _voucher_hash_lock_lock();
268 _dispatch_voucher_debug("corruption", v
);
269 DISPATCH_CLIENT_CRASH(0, "Voucher corruption");
271 // check for resurrection race with _voucher_find_and_retain
272 if (os_atomic_load2o(v
, os_obj_xref_cnt
, ordered
) < 0) {
273 if (_voucher_hash_is_enqueued(v
)) _voucher_hash_remove(kv
, v
);
275 _voucher_hash_lock_unlock();
279 #pragma mark mach_voucher_t
282 _voucher_dealloc_mach_voucher(mach_voucher_t kv
)
284 _dispatch_kvoucher_debug("dealloc", kv
);
285 _dispatch_voucher_debug_machport(kv
);
286 kern_return_t kr
= mach_voucher_deallocate(kv
);
287 DISPATCH_VERIFY_MIG(kr
);
288 (void)dispatch_assume_zero(kr
);
291 static inline kern_return_t
292 _voucher_create_mach_voucher(const mach_voucher_attr_recipe_data_t
*recipes
,
293 size_t recipes_size
, mach_voucher_t
*kvp
)
296 mach_port_t mhp
= _dispatch_get_mach_host_port();
297 mach_voucher_t kv
= MACH_VOUCHER_NULL
;
298 mach_voucher_attr_raw_recipe_array_t kvr
;
299 mach_voucher_attr_recipe_size_t kvr_size
;
300 kvr
= (mach_voucher_attr_raw_recipe_array_t
)recipes
;
301 kvr_size
= (mach_voucher_attr_recipe_size_t
)recipes_size
;
302 kr
= host_create_mach_voucher(mhp
, kvr
, kvr_size
, &kv
);
303 DISPATCH_VERIFY_MIG(kr
);
305 _dispatch_kvoucher_debug("create", kv
);
306 _dispatch_voucher_debug_machport(kv
);
313 _voucher_task_mach_voucher_init(void* ctxt DISPATCH_UNUSED
)
316 mach_voucher_t kv
= MACH_VOUCHER_NULL
;
317 #if !VOUCHER_USE_EMPTY_MACH_BASE_VOUCHER
318 static const mach_voucher_attr_recipe_data_t task_create_recipe
= {
319 .key
= MACH_VOUCHER_ATTR_KEY_BANK
,
320 .command
= MACH_VOUCHER_ATTR_BANK_CREATE
,
322 kr
= _voucher_create_mach_voucher(&task_create_recipe
,
323 sizeof(task_create_recipe
), &kv
);
325 DISPATCH_CLIENT_CRASH(kr
, "Could not create task mach voucher");
327 _voucher_default_task_mach_voucher
= kv
;
329 _voucher_task_mach_voucher
= kv
;
333 voucher_replace_default_voucher(void)
335 (void)_voucher_get_task_mach_voucher(); // initalize task mach voucher
336 mach_voucher_t kv
, tkv
= MACH_VOUCHER_NULL
;
337 voucher_t v
= _voucher_get();
338 if (v
&& v
->v_kvoucher
) {
340 kv
= v
->v_ipc_kvoucher
? v
->v_ipc_kvoucher
: v
->v_kvoucher
;
341 const mach_voucher_attr_recipe_data_t task_copy_recipe
= {
342 .key
= MACH_VOUCHER_ATTR_KEY_BANK
,
343 .command
= MACH_VOUCHER_ATTR_COPY
,
344 .previous_voucher
= kv
,
346 kr
= _voucher_create_mach_voucher(&task_copy_recipe
,
347 sizeof(task_copy_recipe
), &tkv
);
348 if (dispatch_assume_zero(kr
)) {
349 tkv
= MACH_VOUCHER_NULL
;
352 if (!tkv
) tkv
= _voucher_default_task_mach_voucher
;
353 kv
= os_atomic_xchg(&_voucher_task_mach_voucher
, tkv
, relaxed
);
354 if (kv
&& kv
!= _voucher_default_task_mach_voucher
) {
355 _voucher_dealloc_mach_voucher(kv
);
357 _dispatch_voucher_debug("kvoucher[0x%08x] replace default voucher", v
, tkv
);
360 #define _voucher_mach_recipe_size(payload_size) \
361 (sizeof(mach_voucher_attr_recipe_data_t) + (payload_size))
363 #define _voucher_mach_recipe_alloca(v) ((mach_voucher_attr_recipe_t)alloca(\
364 _voucher_mach_recipe_size(0) + \
365 _voucher_mach_recipe_size(sizeof(ipc_pthread_priority_value_t)) + \
366 _voucher_mach_recipe_size(sizeof(_voucher_mach_udata_s)) + \
367 _voucher_extra_size(v)))
369 DISPATCH_ALWAYS_INLINE
370 static inline mach_voucher_attr_recipe_size_t
371 _voucher_mach_recipe_init(mach_voucher_attr_recipe_t mvar_buf
, voucher_s
*v
,
372 mach_voucher_t kvb
, pthread_priority_t pp
)
374 mach_voucher_attr_recipe_size_t extra
= _voucher_extra_size(v
);
375 mach_voucher_attr_recipe_size_t size
= 0;
377 // normalize to just the QoS class and 0 relative priority
378 pp
&= _PTHREAD_PRIORITY_QOS_CLASS_MASK
;
379 if (pp
) pp
|= _PTHREAD_PRIORITY_PRIORITY_MASK
;
381 *mvar_buf
++ = (mach_voucher_attr_recipe_data_t
){
382 .key
= MACH_VOUCHER_ATTR_KEY_ALL
,
383 .command
= MACH_VOUCHER_ATTR_COPY
,
384 .previous_voucher
= kvb
,
386 size
+= _voucher_mach_recipe_size(0);
389 ipc_pthread_priority_value_t value
= (ipc_pthread_priority_value_t
)pp
;
390 *mvar_buf
++ = (mach_voucher_attr_recipe_data_t
){
391 .key
= MACH_VOUCHER_ATTR_KEY_PTHPRIORITY
,
392 .command
= MACH_VOUCHER_ATTR_PTHPRIORITY_CREATE
,
393 .content_size
= sizeof(value
),
395 mvar_buf
= _dispatch_memappend(mvar_buf
, &value
);
396 size
+= _voucher_mach_recipe_size(sizeof(value
));
399 if ((v
&& v
->v_activity
) || pp
) {
400 _voucher_mach_udata_s
*udata_buf
;
401 unsigned udata_size
= 0;
403 if (v
&& v
->v_activity
) {
404 udata_size
= offsetof(_voucher_mach_udata_s
, _vmu_after_activity
);
406 udata_size
= offsetof(_voucher_mach_udata_s
, _vmu_after_priority
);
408 *mvar_buf
= (mach_voucher_attr_recipe_data_t
){
409 .key
= MACH_VOUCHER_ATTR_KEY_USER_DATA
,
410 .command
= MACH_VOUCHER_ATTR_USER_DATA_STORE
,
411 .content_size
= udata_size
,
413 udata_buf
= (_voucher_mach_udata_s
*)(mvar_buf
->content
);
415 if (v
&& v
->v_activity
) {
416 *udata_buf
= (_voucher_mach_udata_s
){
417 .vmu_magic
= VOUCHER_MAGIC_V3
,
418 .vmu_priority
= (_voucher_priority_t
)pp
,
419 .vmu_activity
= v
->v_activity
,
420 .vmu_activity_pid
= v
->v_activity_creator
,
421 .vmu_parent_activity
= v
->v_parent_activity
,
424 *udata_buf
= (_voucher_mach_udata_s
){
425 .vmu_magic
= VOUCHER_MAGIC_V3
,
426 .vmu_priority
= (_voucher_priority_t
)pp
,
430 mvar_buf
= (mach_voucher_attr_recipe_t
)(mvar_buf
->content
+ udata_size
);
431 size
+= _voucher_mach_recipe_size(udata_size
);
435 memcpy(mvar_buf
, _voucher_extra_recipes(v
), extra
);
442 _voucher_get_mach_voucher(voucher_t voucher
)
444 if (!voucher
) return MACH_VOUCHER_NULL
;
445 if (voucher
->v_ipc_kvoucher
) return voucher
->v_ipc_kvoucher
;
446 mach_voucher_t kvb
= voucher
->v_kvoucher
;
447 if (!kvb
) kvb
= _voucher_get_task_mach_voucher();
448 if (!voucher
->v_activity
&& !voucher
->v_priority
&&
449 !_voucher_extra_size(voucher
)) {
453 mach_voucher_attr_recipe_t mvar
= _voucher_mach_recipe_alloca(voucher
);
454 mach_voucher_attr_recipe_size_t size
;
455 mach_voucher_t kv
, kvo
;
458 size
= _voucher_mach_recipe_init(mvar
, voucher
, kvb
, voucher
->v_priority
);
459 kr
= _voucher_create_mach_voucher(mvar
, size
, &kv
);
460 if (dispatch_assume_zero(kr
) || !kv
) {
461 return MACH_VOUCHER_NULL
;
463 if (!os_atomic_cmpxchgv2o(voucher
, v_ipc_kvoucher
, MACH_VOUCHER_NULL
,
464 kv
, &kvo
, relaxed
)) {
465 _voucher_dealloc_mach_voucher(kv
);
468 if (kv
== voucher
->v_kvoucher
) {
469 // if v_kvoucher == v_ipc_kvoucher we keep only one reference
470 _voucher_dealloc_mach_voucher(kv
);
472 _voucher_insert(voucher
);
473 _dispatch_voucher_debug("kvoucher[0x%08x] create", voucher
, kv
);
479 _voucher_create_mach_voucher_with_priority(voucher_t voucher
,
480 pthread_priority_t priority
)
482 if (priority
== _voucher_get_priority(voucher
)) {
483 return MACH_VOUCHER_NULL
; // caller will use _voucher_get_mach_voucher
486 mach_voucher_t kv
, kvb
= voucher
? voucher
->v_kvoucher
: MACH_VOUCHER_NULL
;
487 if (!kvb
) kvb
= _voucher_get_task_mach_voucher();
489 mach_voucher_attr_recipe_t mvar
= _voucher_mach_recipe_alloca(voucher
);
490 mach_voucher_attr_recipe_size_t size
;
492 size
= _voucher_mach_recipe_init(mvar
, voucher
, kvb
, priority
);
493 kr
= _voucher_create_mach_voucher(mvar
, size
, &kv
);
494 if (dispatch_assume_zero(kr
) || !kv
) {
495 return MACH_VOUCHER_NULL
;
497 _dispatch_kvoucher_debug("create with priority from voucher[%p]", kv
,
503 _voucher_create_with_mach_voucher(mach_voucher_t kv
, mach_msg_bits_t msgh_bits
)
505 if (!kv
) return NULL
;
507 mach_voucher_attr_recipe_t vr
;
509 mach_voucher_attr_recipe_size_t kvr_size
= 0;
510 mach_voucher_attr_content_size_t udata_sz
= 0;
511 _voucher_mach_udata_s
*udata
= NULL
;
512 voucher_t v
= _voucher_find_and_retain(kv
);
514 _dispatch_voucher_debug("kvoucher[0x%08x] found", v
, kv
);
515 _voucher_dealloc_mach_voucher(kv
);
518 vr_size
= sizeof(*vr
) + sizeof(_voucher_mach_udata_s
);
519 vr
= alloca(vr_size
);
521 kvr_size
= (mach_voucher_attr_recipe_size_t
)vr_size
;
522 kr
= mach_voucher_extract_attr_recipe(kv
,
523 MACH_VOUCHER_ATTR_KEY_USER_DATA
, (void*)vr
, &kvr_size
);
524 DISPATCH_VERIFY_MIG(kr
);
525 if (!dispatch_assume_zero(kr
) && kvr_size
>= sizeof(*vr
)) {
526 udata_sz
= vr
->content_size
;
527 udata
= (_voucher_mach_udata_s
*)vr
->content
;
528 dispatch_assume(udata_sz
>= sizeof(_voucher_magic_t
));
533 v
= _voucher_alloc(0);
534 v
->v_ipc_kvoucher
= v
->v_kvoucher
= kv
;
535 v
->v_kv_has_importance
= !!(msgh_bits
& MACH_MSGH_BITS_RAISEIMP
);
537 if (udata_sz
>= offsetof(_voucher_mach_udata_s
,_vmu_after_priority
)){
538 if (udata
->vmu_magic
== VOUCHER_MAGIC_V3
) {
539 v
->v_priority
= udata
->vmu_priority
;
542 bool remove_kv_userdata
= false;
543 if (udata_sz
>= offsetof(_voucher_mach_udata_s
, _vmu_after_activity
)) {
545 remove_kv_userdata
= true;
547 if (udata
->vmu_magic
== VOUCHER_MAGIC_V3
&& udata
->vmu_activity
) {
548 v
->v_activity
= udata
->vmu_activity
;
549 v
->v_activity_creator
= udata
->vmu_activity_pid
;
550 v
->v_parent_activity
= udata
->vmu_parent_activity
;
554 if (remove_kv_userdata
) {
555 mach_voucher_t nkv
= MACH_VOUCHER_NULL
;
556 const mach_voucher_attr_recipe_data_t remove_userdata_recipe
[] = {
558 .key
= MACH_VOUCHER_ATTR_KEY_ALL
,
559 .command
= MACH_VOUCHER_ATTR_COPY
,
560 .previous_voucher
= kv
,
563 .key
= MACH_VOUCHER_ATTR_KEY_USER_DATA
,
564 .command
= MACH_VOUCHER_ATTR_REMOVE
,
567 .key
= MACH_VOUCHER_ATTR_KEY_PTHPRIORITY
,
568 .command
= MACH_VOUCHER_ATTR_REMOVE
,
571 mach_voucher_attr_recipe_size_t size
= sizeof(remove_userdata_recipe
);
572 kr
= _voucher_create_mach_voucher(remove_userdata_recipe
, size
, &nkv
);
573 if (!dispatch_assume_zero(kr
)) {
574 _dispatch_voucher_debug("kvoucher[0x%08x] udata removal "
575 "(created 0x%08x)", v
, kv
, nkv
);
576 v
->v_ipc_kvoucher
= MACH_VOUCHER_NULL
;
578 v
->v_kvbase
= _voucher_find_and_retain(nkv
);
580 _voucher_dealloc_mach_voucher(nkv
); // borrow base reference
582 _voucher_dealloc_mach_voucher(kv
);
585 _dispatch_voucher_debug_machport(kv
);
589 _voucher_trace(CREATE
, v
, v
->v_kvoucher
, v
->v_activity
);
591 _dispatch_voucher_debug("kvoucher[0x%08x] create", v
, kv
);
596 _voucher_create_with_priority_and_mach_voucher(voucher_t ov
,
597 pthread_priority_t priority
, mach_voucher_t kv
)
599 if (priority
== _voucher_get_priority(ov
)) {
600 if (kv
) _voucher_dealloc_mach_voucher(kv
);
601 return ov
? _voucher_retain(ov
) : NULL
;
603 voucher_t v
= _voucher_find_and_retain(kv
);
604 voucher_fields_t ignore_fields
= VOUCHER_FIELD_PRIORITY
;
607 _dispatch_voucher_debug("kvoucher[0x%08x] find", v
, kv
);
608 _voucher_dealloc_mach_voucher(kv
);
612 if (kv
) ignore_fields
|= VOUCHER_FIELD_KVOUCHER
;
613 v
= _voucher_clone(ov
, ignore_fields
);
615 v
->v_priority
= (_voucher_priority_t
)priority
;
618 v
->v_ipc_kvoucher
= v
->v_kvoucher
= kv
;
620 _dispatch_voucher_debug("kvoucher[0x%08x] create with priority from "
621 "voucher[%p]", v
, kv
, ov
);
622 _dispatch_voucher_debug_machport(kv
);
624 _voucher_trace(CREATE
, v
, v
->v_kvoucher
, v
->v_activity
);
629 _voucher_create_without_importance(voucher_t ov
)
631 // Nothing to do unless the old voucher has a kernel voucher. If it
632 // doesn't, it can't have any importance, now or in the future.
633 if (!ov
) return NULL
;
634 if (!ov
->v_kvoucher
|| !ov
->v_kv_has_importance
) return _voucher_retain(ov
);
636 mach_voucher_t kv
, okv
;
637 // Copy kernel voucher, removing importance.
638 okv
= ov
->v_ipc_kvoucher
? ov
->v_ipc_kvoucher
: ov
->v_kvoucher
;
639 const mach_voucher_attr_recipe_data_t importance_remove_recipe
[] = {
641 .key
= MACH_VOUCHER_ATTR_KEY_ALL
,
642 .command
= MACH_VOUCHER_ATTR_COPY
,
643 .previous_voucher
= okv
,
646 .key
= MACH_VOUCHER_ATTR_KEY_IMPORTANCE
,
647 .command
= MACH_VOUCHER_ATTR_REMOVE
,
650 kr
= _voucher_create_mach_voucher(importance_remove_recipe
,
651 sizeof(importance_remove_recipe
), &kv
);
652 if (dispatch_assume_zero(kr
) || !kv
) {
653 if (ov
->v_ipc_kvoucher
) return NULL
;
654 kv
= MACH_VOUCHER_NULL
;
657 _voucher_dealloc_mach_voucher(kv
);
658 return _voucher_retain(ov
);
660 voucher_t v
= _voucher_find_and_retain(kv
);
661 if (v
&& ov
->v_ipc_kvoucher
) {
662 _dispatch_voucher_debug("kvoucher[0x%08x] find without importance "
663 "from voucher[%p]", v
, kv
, ov
);
664 _voucher_dealloc_mach_voucher(kv
);
667 voucher_t kvbase
= v
;
668 voucher_fields_t ignore_fields
= VOUCHER_FIELD_KVOUCHER
;
669 v
= _voucher_clone(ov
, ignore_fields
);
671 if (ov
->v_ipc_kvoucher
) {
672 v
->v_ipc_kvoucher
= kv
;
675 v
->v_kvbase
= kvbase
;
676 _voucher_dealloc_mach_voucher(kv
); // borrow base reference
679 _dispatch_voucher_debug("kvoucher[0x%08x] create without importance "
680 "from voucher[%p]", v
, kv
, ov
);
682 _voucher_trace(CREATE
, v
, v
->v_kvoucher
, v
->v_activity
);
687 _voucher_create_accounting_voucher(voucher_t ov
)
689 // Nothing to do unless the old voucher has a kernel voucher. If it does
690 // doesn't, it can't have any accounting attributes.
691 if (!ov
|| !ov
->v_kvoucher
) return NULL
;
692 kern_return_t kr
= KERN_SUCCESS
;
693 mach_voucher_t okv
, kv
= MACH_VOUCHER_NULL
;
694 okv
= ov
->v_ipc_kvoucher
? ov
->v_ipc_kvoucher
: ov
->v_kvoucher
;
695 const mach_voucher_attr_recipe_data_t accounting_copy_recipe
= {
696 .key
= MACH_VOUCHER_ATTR_KEY_BANK
,
697 .command
= MACH_VOUCHER_ATTR_COPY
,
698 .previous_voucher
= okv
,
700 kr
= _voucher_create_mach_voucher(&accounting_copy_recipe
,
701 sizeof(accounting_copy_recipe
), &kv
);
702 if (dispatch_assume_zero(kr
) || !kv
) {
705 voucher_t v
= _voucher_find_and_retain(kv
);
707 _dispatch_voucher_debug("kvoucher[0x%08x] find accounting voucher "
708 "from voucher[%p]", v
, kv
, ov
);
709 _voucher_dealloc_mach_voucher(kv
);
712 v
= _voucher_alloc(0);
713 v
->v_ipc_kvoucher
= v
->v_kvoucher
= kv
;
715 v
->v_kvbase
= _voucher_retain(ov
);
716 _voucher_dealloc_mach_voucher(kv
); // borrow base reference
718 _voucher_trace(CREATE
, v
, kv
, v
->v_activity
);
720 _dispatch_voucher_debug("kvoucher[0x%08x] create accounting voucher "
721 "from voucher[%p]", v
, kv
, ov
);
726 voucher_create_with_mach_msg(mach_msg_header_t
*msg
)
728 mach_msg_bits_t msgh_bits
;
729 mach_voucher_t kv
= _voucher_mach_msg_get(msg
, &msgh_bits
);
730 return _voucher_create_with_mach_voucher(kv
, msgh_bits
);
734 voucher_decrement_importance_count4CF(voucher_t v
)
736 if (!v
|| !v
->v_kvoucher
|| !v
->v_kv_has_importance
) return;
738 mach_voucher_t kv
= v
->v_ipc_kvoucher
? v
->v_ipc_kvoucher
: v
->v_kvoucher
;
740 mach_voucher_attr_content_t kvc_in
= (mach_voucher_attr_content_t
)&dec
;
741 mach_voucher_attr_content_size_t kvc_in_size
= sizeof(dec
);
742 mach_voucher_attr_content_t kvc_out
= NULL
;
743 mach_voucher_attr_content_size_t kvc_out_size
= 0;
745 uint32_t count
= UINT32_MAX
;
746 kvc_out
= (mach_voucher_attr_content_t
)&count
;
747 kvc_out_size
= sizeof(count
);
749 kr
= mach_voucher_attr_command(kv
, MACH_VOUCHER_ATTR_KEY_IMPORTANCE
,
750 MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL
, kvc_in
, kvc_in_size
,
751 kvc_out
, &kvc_out_size
);
752 DISPATCH_VERIFY_MIG(kr
);
753 if (kr
== KERN_INVALID_TASK
) return; // non-denap receiver rdar://25643185
755 _dispatch_voucher_debug("kvoucher[0x%08x] decrement importance count to %u:"
756 " %s - 0x%x", v
, kv
, count
, mach_error_string(kr
), kr
);
758 if (slowpath(dispatch_assume_zero(kr
) == KERN_FAILURE
)) {
759 DISPATCH_CLIENT_CRASH(kr
, "Voucher importance count underflow");
763 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
765 voucher_get_mach_voucher(voucher_t voucher
)
767 return _voucher_get_mach_voucher(voucher
);
772 _voucher_xref_dispose(voucher_t voucher
)
774 _dispatch_voucher_debug("xref_dispose", voucher
);
775 _voucher_remove(voucher
);
776 return _os_object_release_internal_n_inline((_os_object_t
)voucher
, 1);
780 _voucher_dispose(voucher_t voucher
)
782 _voucher_trace(DISPOSE
, voucher
);
783 _dispatch_voucher_debug("dispose", voucher
);
784 if (slowpath(_voucher_hash_is_enqueued(voucher
))) {
785 _dispatch_voucher_debug("corruption", voucher
);
786 DISPATCH_CLIENT_CRASH(0, "Voucher corruption");
788 _voucher_hash_mark_not_enqueued(voucher
);
789 if (voucher
->v_ipc_kvoucher
) {
790 if (voucher
->v_ipc_kvoucher
!= voucher
->v_kvoucher
) {
791 _voucher_dealloc_mach_voucher(voucher
->v_ipc_kvoucher
);
793 voucher
->v_ipc_kvoucher
= MACH_VOUCHER_NULL
;
795 if (voucher
->v_kvoucher
) {
796 if (!voucher
->v_kvbase
) {
797 _voucher_dealloc_mach_voucher(voucher
->v_kvoucher
);
799 voucher
->v_kvoucher
= MACH_VOUCHER_NULL
;
801 if (voucher
->v_kvbase
) {
802 _voucher_release(voucher
->v_kvbase
);
803 voucher
->v_kvbase
= NULL
;
805 voucher
->v_activity
= 0;
806 voucher
->v_activity_creator
= 0;
807 voucher
->v_parent_activity
= 0;
808 voucher
->v_priority
= 0;
809 #if VOUCHER_ENABLE_RECIPE_OBJECTS
810 voucher
->v_recipe_extra_size
= 0;
811 voucher
->v_recipe_extra_offset
= 0;
813 return _os_object_dealloc((_os_object_t
)voucher
);
817 _voucher_activity_debug_channel_barrier_nop(void *ctxt DISPATCH_UNUSED
)
822 _voucher_activity_debug_channel_init(void)
824 dispatch_mach_handler_function_t handler
= NULL
;
826 if (_voucher_libtrace_hooks
) {
827 handler
= _voucher_libtrace_hooks
->vah_debug_channel_handler
;
829 if (!handler
) return;
835 kr
= task_get_debug_control_port(mach_task_self(), &dbgp
);
836 DISPATCH_VERIFY_MIG(kr
);
838 DISPATCH_CLIENT_CRASH(kr
, "Couldn't get debug control port");
841 dm
= dispatch_mach_create_f("com.apple.debug-channel",
842 DISPATCH_TARGET_QUEUE_DEFAULT
, NULL
, handler
);
843 dm
->dm_recv_refs
->du_can_be_wlh
= false; // 29906118
844 dispatch_mach_connect(dm
, dbgp
, MACH_PORT_NULL
, NULL
);
845 // will force the DISPATCH_MACH_CONNECTED event
846 dispatch_mach_send_barrier_f(dm
, NULL
,
847 _voucher_activity_debug_channel_barrier_nop
);
848 _voucher_activity_debug_channel
= dm
;
853 _voucher_atfork_child(void)
855 _dispatch_thread_setspecific(dispatch_voucher_key
, NULL
);
856 _voucher_task_mach_voucher_pred
= 0;
857 _voucher_task_mach_voucher
= MACH_VOUCHER_NULL
;
858 #if !VOUCHER_USE_EMPTY_MACH_BASE_VOUCHER
859 _voucher_default_task_mach_voucher
= MACH_PORT_NULL
;
861 _voucher_aid_next
= 0;
862 _firehose_task_buffer_pred
= 0;
863 _firehose_task_buffer
= NULL
; // firehose buffer is VM_INHERIT_NONE
866 #if VOUCHER_EXPORT_PERSONA_SPI
867 #if VOUCHER_USE_PERSONA
869 _voucher_get_current_persona_token(struct persona_token
*token
)
871 kern_return_t kr
= KERN_FAILURE
;
872 voucher_t v
= _voucher_get();
874 if (v
&& v
->v_kvoucher
) {
875 mach_voucher_t kv
= v
->v_ipc_kvoucher
?: v
->v_kvoucher
;
876 mach_voucher_attr_content_t kvc_in
= NULL
;
877 mach_voucher_attr_content_size_t kvc_in_size
= 0;
878 mach_voucher_attr_content_t kvc_out
=
879 (mach_voucher_attr_content_t
)token
;
880 mach_voucher_attr_content_size_t kvc_out_size
= sizeof(*token
);
882 kr
= mach_voucher_attr_command(kv
, MACH_VOUCHER_ATTR_KEY_BANK
,
883 BANK_PERSONA_TOKEN
, kvc_in
, kvc_in_size
,
884 kvc_out
, &kvc_out_size
);
885 if (kr
!= KERN_NOT_SUPPORTED
886 // Voucher doesn't have a PERSONA_TOKEN
887 && kr
!= KERN_INVALID_VALUE
888 // Kernel doesn't understand BANK_PERSONA_TOKEN
889 && kr
!= KERN_INVALID_ARGUMENT
) {
890 (void)dispatch_assume_zero(kr
);
898 voucher_get_current_persona(void)
900 uid_t persona_id
= PERSONA_ID_NONE
;
902 #if VOUCHER_USE_PERSONA
903 struct persona_token token
;
906 if (_voucher_get_current_persona_token(&token
) == KERN_SUCCESS
) {
907 return token
.originator
.persona_id
;
910 // falling back to the process persona if there is no adopted voucher
911 if (kpersona_get(&persona_id
) < 0) {
914 (void)dispatch_assume_zero(err
);
922 voucher_get_current_persona_originator_info(struct proc_persona_info
*persona_info
)
924 #if VOUCHER_USE_PERSONA
925 struct persona_token token
;
926 if (_voucher_get_current_persona_token(&token
) == KERN_SUCCESS
) {
927 *persona_info
= token
.originator
;
937 voucher_get_current_persona_proximate_info(struct proc_persona_info
*persona_info
)
939 #if VOUCHER_USE_PERSONA
940 struct persona_token token
;
941 if (_voucher_get_current_persona_token(&token
) == KERN_SUCCESS
) {
942 *persona_info
= token
.proximate
;
953 #pragma mark _voucher_init
956 voucher_mach_msg_set(mach_msg_header_t
*msg
)
958 return _voucher_mach_msg_set(msg
, _voucher_get());
962 voucher_mach_msg_clear(mach_msg_header_t
*msg
)
964 (void)_voucher_mach_msg_clear(msg
, false);
967 voucher_mach_msg_state_t
968 voucher_mach_msg_adopt(mach_msg_header_t
*msg
)
970 mach_msg_bits_t msgh_bits
;
971 mach_voucher_t kv
= _voucher_mach_msg_get(msg
, &msgh_bits
);
972 if (!kv
) return VOUCHER_MACH_MSG_STATE_UNCHANGED
;
973 voucher_t v
= _voucher_create_with_mach_voucher(kv
, msgh_bits
);
974 return (voucher_mach_msg_state_t
)_voucher_adopt(v
);
978 voucher_mach_msg_revert(voucher_mach_msg_state_t state
)
980 if (state
== VOUCHER_MACH_MSG_STATE_UNCHANGED
) return;
981 _voucher_replace((voucher_t
)state
);
984 #if DISPATCH_USE_LIBKERNEL_VOUCHER_INIT
985 #include <_libkernel_init.h>
987 static const struct _libkernel_voucher_functions _voucher_libkernel_functions
=
990 .voucher_mach_msg_set
= voucher_mach_msg_set
,
991 .voucher_mach_msg_clear
= voucher_mach_msg_clear
,
992 .voucher_mach_msg_adopt
= voucher_mach_msg_adopt
,
993 .voucher_mach_msg_revert
= voucher_mach_msg_revert
,
997 _voucher_libkernel_init(void)
999 kern_return_t kr
= __libkernel_voucher_init(&_voucher_libkernel_functions
);
1000 dispatch_assert(!kr
);
1003 #define _voucher_libkernel_init()
1007 voucher_activity_initialize_4libtrace(voucher_activity_hooks_t hooks
)
1009 if (hooks
->vah_version
< 3) {
1010 DISPATCH_CLIENT_CRASH(hooks
->vah_version
, "unsupported vah_version");
1012 if (!os_atomic_cmpxchg(&_voucher_libtrace_hooks
, NULL
,
1014 DISPATCH_CLIENT_CRASH(_voucher_libtrace_hooks
,
1015 "voucher_activity_initialize_4libtrace called twice");
1022 _voucher_libkernel_init();
1024 for (i
= 0; i
< VL_HASH_SIZE
; i
++) {
1025 _voucher_hash_head_init(&_voucher_hash
[i
]);
1030 #pragma mark voucher_activity_t
1034 _voucher_activity_id_allocate_slow(uint64_t aid
)
1039 kr
= mach_generate_activity_id(mach_task_self(), 1, &next
);
1041 DISPATCH_CLIENT_CRASH(kr
, "Could not generate an activity ID");
1043 next
*= MACH_ACTIVITY_ID_RANGE_SIZE
;
1044 next
&= MACH_ACTIVITY_ID_MASK
;
1045 if (unlikely(next
== 0)) {
1049 if (unlikely(aid
== 0)) {
1050 if (os_atomic_cmpxchg(&_voucher_aid_next
, 0, next
+ 1, relaxed
)) {
1054 return os_atomic_xchg(&_voucher_aid_next
, next
, relaxed
);
1057 DISPATCH_ALWAYS_INLINE
1058 static firehose_activity_id_t
1059 _voucher_activity_id_allocate(firehose_activity_flags_t flags
)
1062 os_atomic_rmw_loop(&_voucher_aid_next
, aid
, next
, relaxed
, {
1064 if (aid
== 0 || next
% MACH_ACTIVITY_ID_RANGE_SIZE
== 0) {
1065 os_atomic_rmw_loop_give_up({
1066 aid
= _voucher_activity_id_allocate_slow(aid
);
1071 return FIREHOSE_ACTIVITY_ID_MAKE(aid
, flags
);
1074 firehose_activity_id_t
1075 voucher_activity_id_allocate(firehose_activity_flags_t flags
)
1077 return _voucher_activity_id_allocate(flags
);
1080 #define _voucher_activity_tracepoint_reserve(stamp, stream, pub, priv, privbuf) \
1081 firehose_buffer_tracepoint_reserve(_firehose_task_buffer, stamp, \
1082 stream, pub, priv, privbuf)
1084 #define _voucher_activity_tracepoint_flush(ft, ftid) \
1085 firehose_buffer_tracepoint_flush(_firehose_task_buffer, ft, ftid)
1089 _firehose_task_buffer_init(void *ctx OS_UNUSED
)
1091 mach_port_t logd_port
;
1093 /* Query the uniquepid of the current process */
1094 struct proc_uniqidentifierinfo p_uniqinfo
= { };
1097 info_size
= proc_pidinfo(getpid(), PROC_PIDUNIQIDENTIFIERINFO
, 1,
1098 &p_uniqinfo
, PROC_PIDUNIQIDENTIFIERINFO_SIZE
);
1099 if (slowpath(info_size
!= PROC_PIDUNIQIDENTIFIERINFO_SIZE
)) {
1100 if (info_size
== 0) {
1101 DISPATCH_INTERNAL_CRASH(errno
,
1102 "Unable to get the unique pid (error)");
1104 DISPATCH_INTERNAL_CRASH(info_size
,
1105 "Unable to get the unique pid (size)");
1108 _voucher_unique_pid
= p_uniqinfo
.p_uniqueid
;
1111 if (!fastpath(_voucher_libtrace_hooks
)) {
1112 if (0) { // <rdar://problem/23393959>
1113 DISPATCH_CLIENT_CRASH(0,
1114 "Activity subsystem isn't initialized yet");
1118 logd_port
= _voucher_libtrace_hooks
->vah_get_logd_port();
1120 unsigned long flags
= 0;
1121 #if DISPATCH_USE_MEMORYPRESSURE_SOURCE
1122 if (_dispatch_memory_warn
) {
1123 flags
|= FIREHOSE_BUFFER_BANK_FLAG_LOW_MEMORY
;
1126 // firehose_buffer_create always consumes the send-right
1127 _firehose_task_buffer
= firehose_buffer_create(logd_port
,
1128 _voucher_unique_pid
, flags
);
1129 if (_voucher_libtrace_hooks
->vah_version
>= 4 &&
1130 _voucher_libtrace_hooks
->vah_metadata_init
) {
1131 firehose_buffer_t fb
= _firehose_task_buffer
;
1132 size_t meta_sz
= FIREHOSE_BUFFER_LIBTRACE_HEADER_SIZE
;
1133 void *meta
= (void *)((uintptr_t)(&fb
->fb_header
+ 1) - meta_sz
);
1134 _voucher_libtrace_hooks
->vah_metadata_init(meta
, meta_sz
);
1139 DISPATCH_ALWAYS_INLINE
1141 _voucher_activity_disabled(void)
1143 dispatch_once_f(&_firehose_task_buffer_pred
,
1144 NULL
, _firehose_task_buffer_init
);
1146 firehose_buffer_t fb
= _firehose_task_buffer
;
1148 return slowpath(fb
->fb_header
.fbh_sendp
== MACH_PORT_DEAD
);
1154 voucher_activity_get_metadata_buffer(size_t *length
)
1156 if (_voucher_activity_disabled()) {
1161 firehose_buffer_header_t fbh
= &_firehose_task_buffer
->fb_header
;
1163 *length
= FIREHOSE_BUFFER_LIBTRACE_HEADER_SIZE
;
1164 return (void *)((uintptr_t)(fbh
+ 1) - *length
);
1168 voucher_activity_create_with_data(firehose_tracepoint_id_t
*trace_id
,
1169 voucher_t base
, firehose_activity_flags_t flags
,
1170 const void *pubdata
, size_t publen
)
1172 firehose_activity_id_t va_id
= 0, current_id
= 0, parent_id
= 0;
1173 firehose_tracepoint_id_u ftid
= { .ftid_value
= *trace_id
};
1174 uint64_t creator_id
= 0;
1176 voucher_t ov
= _voucher_get();
1179 if (os_add_overflow(sizeof(va_id
), publen
, &pubsize
) || pubsize
> 128) {
1180 DISPATCH_CLIENT_CRASH(pubsize
, "Absurd publen");
1182 if (base
== VOUCHER_CURRENT
) {
1186 FIREHOSE_TRACE_ID_CLEAR_FLAG(ftid
, base
, has_unique_pid
);
1187 if (ov
&& (current_id
= ov
->v_activity
)) {
1188 FIREHOSE_TRACE_ID_SET_FLAG(ftid
, base
, has_current_aid
);
1189 pubsize
+= sizeof(firehose_activity_id_t
);
1190 if ((creator_id
= ov
->v_activity_creator
)) {
1191 FIREHOSE_TRACE_ID_SET_FLAG(ftid
, base
, has_unique_pid
);
1192 pubsize
+= sizeof(uint64_t);
1195 if (base
!= VOUCHER_NULL
) {
1196 parent_id
= base
->v_activity
;
1200 FIREHOSE_TRACE_ID_SET_FLAG(ftid
, activity
, has_other_aid
);
1201 pubsize
+= sizeof(firehose_activity_id_t
);
1202 flags
|= FIREHOSE_ACTIVITY_ID_FLAGS(parent_id
);
1205 if (firehose_precise_timestamps_enabled()) {
1206 flags
|= firehose_activity_flags_precise_timestamp
;
1208 voucher_fields_t ignore_fields
= VOUCHER_FIELD_ACTIVITY
;
1209 v
= _voucher_clone(base
, ignore_fields
);
1210 v
->v_activity
= va_id
= _voucher_activity_id_allocate(flags
);
1211 v
->v_activity_creator
= _voucher_unique_pid
;
1212 v
->v_parent_activity
= parent_id
;
1214 if (_voucher_activity_disabled()) {
1218 static const firehose_stream_t streams
[2] = {
1219 firehose_stream_metadata
,
1220 firehose_stream_persist
,
1222 firehose_tracepoint_t ft
;
1223 uint64_t stamp
= firehose_tracepoint_time(flags
);
1225 for (size_t i
= 0; i
< countof(streams
); i
++) {
1226 ft
= _voucher_activity_tracepoint_reserve(stamp
, streams
[i
], pubsize
,
1228 if (!fastpath(ft
)) continue;
1230 uint8_t *pubptr
= ft
->ft_data
;
1232 pubptr
= _dispatch_memappend(pubptr
, ¤t_id
);
1235 pubptr
= _dispatch_memappend(pubptr
, &creator_id
);
1238 pubptr
= _dispatch_memappend(pubptr
, &parent_id
);
1240 pubptr
= _dispatch_memappend(pubptr
, &va_id
);
1241 pubptr
= _dispatch_mempcpy(pubptr
, pubdata
, publen
);
1242 _voucher_activity_tracepoint_flush(ft
, ftid
);
1245 *trace_id
= ftid
.ftid_value
;
1246 _voucher_trace(CREATE
, v
, v
->v_kvoucher
, va_id
);
1251 voucher_activity_create_with_location(firehose_tracepoint_id_t
*trace_id
,
1252 voucher_t base
, firehose_activity_flags_t flags
, uint64_t loc
)
1254 return voucher_activity_create_with_data(trace_id
, base
, flags
,
1258 #if OS_VOUCHER_ACTIVITY_GENERATE_SWAPS
1260 _voucher_activity_swap(firehose_activity_id_t old_id
,
1261 firehose_activity_id_t new_id
)
1263 if (_voucher_activity_disabled()) return;
1265 firehose_tracepoint_id_u ftid
= { .ftid
= {
1266 ._namespace
= firehose_tracepoint_namespace_activity
,
1267 ._type
= _firehose_tracepoint_type_activity_swap
,
1269 uint16_t pubsize
= 0;
1272 FIREHOSE_TRACE_ID_SET_FLAG(ftid
, base
, has_current_aid
);
1273 pubsize
+= sizeof(firehose_activity_id_t
);
1276 FIREHOSE_TRACE_ID_SET_FLAG(ftid
, activity
, has_other_aid
);
1277 pubsize
+= sizeof(firehose_activity_id_t
);
1280 firehose_stream_t stream
= firehose_stream_metadata
;
1281 firehose_tracepoint_t ft
;
1282 firehose_activity_flags_t flags
= FIREHOSE_ACTIVITY_ID_FLAGS(old_id
) |
1283 FIREHOSE_ACTIVITY_ID_FLAGS(new_id
);
1284 uint64_t stamp
= firehose_tracepoint_time(flags
);
1286 _dispatch_voucher_ktrace_activity_adopt(new_id
);
1288 ft
= _voucher_activity_tracepoint_reserve(stamp
, stream
, pubsize
, 0, NULL
);
1289 if (!fastpath(ft
)) return;
1290 uint8_t *pubptr
= ft
->ft_data
;
1291 if (old_id
) pubptr
= _dispatch_memappend(pubptr
, &old_id
);
1292 if (new_id
) pubptr
= _dispatch_memappend(pubptr
, &new_id
);
1293 _voucher_activity_tracepoint_flush(ft
, ftid
);
1297 firehose_activity_id_t
1298 voucher_get_activity_id_and_creator(voucher_t v
, uint64_t *creator_pid
,
1299 firehose_activity_id_t
*parent_id
)
1301 if (v
== VOUCHER_CURRENT
) {
1304 if (v
== VOUCHER_NULL
) {
1305 if (creator_pid
) *creator_pid
= 0;
1306 if (parent_id
) *parent_id
= FIREHOSE_ACTIVITY_ID_NULL
;
1307 return FIREHOSE_ACTIVITY_ID_NULL
;
1309 if (creator_pid
) *creator_pid
= v
->v_activity_creator
;
1310 if (parent_id
) *parent_id
= v
->v_parent_activity
;
1311 return v
->v_activity
;
1314 firehose_activity_id_t
1315 voucher_get_activity_id(voucher_t v
, firehose_activity_id_t
*parent_id
)
1317 return voucher_get_activity_id_and_creator(v
, NULL
, parent_id
);
1321 voucher_activity_flush(firehose_stream_t stream
)
1323 if (_voucher_activity_disabled()) return;
1324 firehose_buffer_stream_flush(_firehose_task_buffer
, stream
);
1328 firehose_tracepoint_id_t
1329 voucher_activity_trace_v(firehose_stream_t stream
,
1330 firehose_tracepoint_id_t trace_id
, uint64_t stamp
,
1331 const struct iovec
*iov
, size_t publen
, size_t privlen
)
1333 firehose_tracepoint_id_u ftid
= { .ftid_value
= trace_id
};
1334 const uint16_t ft_size
= offsetof(struct firehose_tracepoint_s
, ft_data
);
1335 const size_t _firehose_chunk_payload_size
=
1336 sizeof(((struct firehose_chunk_s
*)0)->fc_data
);
1338 if (_voucher_activity_disabled()) return 0;
1340 firehose_tracepoint_t ft
;
1341 firehose_activity_id_t va_id
= 0;
1342 firehose_chunk_t fc
;
1343 uint8_t *privptr
, *pubptr
;
1344 size_t pubsize
= publen
;
1345 voucher_t ov
= _voucher_get();
1346 uint64_t creator_pid
;
1348 if ((va_id
= _voucher_get_activity_id(ov
, &creator_pid
))) {
1349 FIREHOSE_TRACE_ID_SET_FLAG(ftid
, base
, has_current_aid
);
1350 pubsize
+= sizeof(va_id
);
1352 if (FIREHOSE_TRACE_ID_HAS_FLAG(ftid
, base
, has_unique_pid
)) {
1354 pubsize
+= sizeof(creator_pid
);
1356 FIREHOSE_TRACE_ID_CLEAR_FLAG(ftid
, base
, has_unique_pid
);
1363 FIREHOSE_TRACE_ID_SET_FLAG(ftid
, log
, has_private_data
);
1364 pubsize
+= sizeof(struct firehose_buffer_range_s
);
1367 if (slowpath(ft_size
+ pubsize
+ privlen
> _firehose_chunk_payload_size
)) {
1368 DISPATCH_CLIENT_CRASH(ft_size
+ pubsize
+ privlen
, "Log is too large");
1371 ft
= _voucher_activity_tracepoint_reserve(stamp
, stream
, (uint16_t)pubsize
,
1372 (uint16_t)privlen
, &privptr
);
1373 if (!fastpath(ft
)) return 0;
1374 pubptr
= ft
->ft_data
;
1376 pubptr
= _dispatch_memappend(pubptr
, &va_id
);
1379 pubptr
= _dispatch_memappend(pubptr
, &creator_pid
);
1382 fc
= firehose_buffer_chunk_for_address(ft
);
1383 struct firehose_buffer_range_s range
= {
1384 .fbr_offset
= (uint16_t)(privptr
- fc
->fc_start
),
1385 .fbr_length
= (uint16_t)privlen
,
1387 pubptr
= _dispatch_memappend(pubptr
, &range
);
1389 while (publen
> 0) {
1390 pubptr
= _dispatch_mempcpy(pubptr
, iov
->iov_base
, iov
->iov_len
);
1391 if (unlikely(os_sub_overflow(publen
, iov
->iov_len
, &publen
))) {
1392 DISPATCH_CLIENT_CRASH(0, "Invalid arguments");
1396 while (privlen
> 0) {
1397 privptr
= _dispatch_mempcpy(privptr
, iov
->iov_base
, iov
->iov_len
);
1398 if (unlikely(os_sub_overflow(privlen
, iov
->iov_len
, &privlen
))) {
1399 DISPATCH_CLIENT_CRASH(0, "Invalid arguments");
1403 _voucher_activity_tracepoint_flush(ft
, ftid
);
1404 return ftid
.ftid_value
;
1407 firehose_tracepoint_id_t
1408 voucher_activity_trace(firehose_stream_t stream
,
1409 firehose_tracepoint_id_t trace_id
, uint64_t stamp
,
1410 const void *pubdata
, size_t publen
)
1412 struct iovec iov
= { (void *)pubdata
, publen
};
1413 return voucher_activity_trace_v(stream
, trace_id
, stamp
, &iov
, publen
, 0);
1416 firehose_tracepoint_id_t
1417 voucher_activity_trace_with_private_strings(firehose_stream_t stream
,
1418 firehose_tracepoint_id_t trace_id
, uint64_t stamp
,
1419 const void *pubdata
, size_t publen
,
1420 const void *privdata
, size_t privlen
)
1422 struct iovec iov
[2] = {
1423 { (void *)pubdata
, publen
},
1424 { (void *)privdata
, privlen
},
1426 return voucher_activity_trace_v(stream
, trace_id
, stamp
,
1427 iov
, publen
, privlen
);
1431 #pragma mark _voucher_debug
1434 _voucher_debug(voucher_t v
, char* buf
, size_t bufsiz
)
1437 #define bufprintf(...) \
1438 offset += dsnprintf(&buf[offset], bufsiz - offset, ##__VA_ARGS__)
1439 bufprintf("voucher[%p] = { xref = %d, ref = %d", v
,
1440 v
->os_obj_xref_cnt
+ 1, v
->os_obj_ref_cnt
+ 1);
1443 bufprintf(", base voucher %p", v
->v_kvbase
);
1445 if (v
->v_kvoucher
) {
1446 bufprintf(", kvoucher%s 0x%x", v
->v_kvoucher
== v
->v_ipc_kvoucher
?
1447 " & ipc kvoucher" : "", v
->v_kvoucher
);
1449 if (v
->v_ipc_kvoucher
&& v
->v_ipc_kvoucher
!= v
->v_kvoucher
) {
1450 bufprintf(", ipc kvoucher 0x%x", v
->v_ipc_kvoucher
);
1452 if (v
->v_priority
) {
1453 bufprintf(", QOS 0x%x", v
->v_priority
);
1455 if (v
->v_activity
) {
1456 bufprintf(", activity 0x%llx (pid: 0x%16llx, parent 0x%llx)",
1457 v
->v_activity
, v
->v_activity_creator
, v
->v_parent_activity
);
1463 #else // VOUCHER_USE_MACH_VOUCHER
1466 #pragma mark Simulator / vouchers disabled
1468 #if VOUCHER_ENABLE_RECIPE_OBJECTS
1470 voucher_create(voucher_recipe_t recipe
)
1475 #endif // VOUCHER_ENABLE_RECIPE_OBJECTS
1478 voucher_adopt(voucher_t voucher
)
1490 voucher_copy_without_importance(void)
1496 voucher_retain(voucher_t voucher
)
1502 voucher_release(voucher_t voucher
)
1508 voucher_replace_default_voucher(void)
1513 voucher_decrement_importance_count4CF(voucher_t v
)
1519 _voucher_thread_cleanup(void *voucher
)
1525 _voucher_dealloc_mach_voucher(mach_voucher_t kv
)
1531 _voucher_get_mach_voucher(voucher_t voucher
)
1534 return MACH_VOUCHER_NULL
;
1538 _voucher_create_mach_voucher_with_priority(voucher_t voucher
,
1539 pthread_priority_t priority
)
1541 (void)voucher
; (void)priority
;
1542 return MACH_VOUCHER_NULL
;
1546 _voucher_create_with_priority_and_mach_voucher(voucher_t voucher
,
1547 pthread_priority_t priority
, mach_voucher_t kv
)
1549 (void)voucher
; (void)priority
; (void)kv
;
1554 _voucher_create_accounting_voucher(voucher_t voucher
)
1562 voucher_create_with_mach_msg(mach_msg_header_t
*msg
)
1569 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
1571 voucher_get_mach_voucher(voucher_t voucher
)
1576 #endif // VOUCHER_ENABLE_GET_MACH_VOUCHER
1579 _voucher_xref_dispose(voucher_t voucher
)
1585 _voucher_dispose(voucher_t voucher
)
1590 #if VOUCHER_EXPORT_PERSONA_SPI
1592 voucher_get_current_persona(void)
1594 return PERSONA_ID_NONE
;
1598 voucher_get_current_persona_originator_info(struct proc_persona_info
*persona_info
)
1605 voucher_get_current_persona_proximate_info(struct proc_persona_info
*persona_info
)
1610 #endif // VOUCHER_EXPORT_PERSONA_SPI
1613 _voucher_activity_debug_channel_init(void)
1618 _voucher_atfork_child(void)
1627 #if OS_VOUCHER_ACTIVITY_SPI
1629 voucher_activity_get_metadata_buffer(size_t *length
)
1636 voucher_activity_create(firehose_tracepoint_id_t trace_id
,
1637 voucher_t base
, firehose_activity_flags_t flags
, uint64_t location
)
1639 (void)trace_id
; (void)base
; (void)flags
; (void)location
;
1644 voucher_activity_create_with_location(firehose_tracepoint_id_t
*trace_id
,
1645 voucher_t base
, firehose_activity_flags_t flags
, uint64_t location
)
1647 (void)trace_id
; (void)base
; (void)flags
; (void)location
;
1651 firehose_activity_id_t
1652 voucher_get_activity_id(voucher_t voucher
, firehose_activity_id_t
*parent_id
)
1654 (void)voucher
; (void)parent_id
;
1658 firehose_activity_id_t
1659 voucher_get_activity_id_and_creator(voucher_t voucher
, uint64_t *creator_pid
,
1660 firehose_activity_id_t
*parent_id
)
1662 if (creator_pid
) *creator_pid
= 0;
1663 (void)voucher
; (void)parent_id
;
1667 firehose_tracepoint_id_t
1668 voucher_activity_trace(firehose_stream_t stream
,
1669 firehose_tracepoint_id_t trace_id
, uint64_t timestamp
,
1670 const void *pubdata
, size_t publen
)
1672 (void)stream
; (void)trace_id
; (void)timestamp
;
1673 (void)pubdata
; (void)publen
;
1677 firehose_tracepoint_id_t
1678 voucher_activity_trace_with_private_strings(firehose_stream_t stream
,
1679 firehose_tracepoint_id_t trace_id
, uint64_t timestamp
,
1680 const void *pubdata
, size_t publen
,
1681 const void *privdata
, size_t privlen
)
1683 (void)stream
; (void)trace_id
; (void)timestamp
;
1684 (void)pubdata
; (void)publen
; (void)privdata
; (void)privlen
;
1688 firehose_tracepoint_id_t
1689 voucher_activity_trace_v(firehose_stream_t stream
,
1690 firehose_tracepoint_id_t trace_id
, uint64_t timestamp
,
1691 const struct iovec
*iov
, size_t publen
, size_t privlen
)
1693 (void)stream
; (void)trace_id
; (void)timestamp
;
1694 (void)iov
; (void)publen
; (void)privlen
;
1699 voucher_activity_flush(firehose_stream_t stream
)
1705 voucher_activity_initialize_4libtrace(voucher_activity_hooks_t hooks
)
1709 #endif // OS_VOUCHER_ACTIVITY_SPI
1712 _voucher_debug(voucher_t v
, char* buf
, size_t bufsiz
)
1714 (void)v
; (void)buf
; (void)bufsiz
;
1718 #endif // VOUCHER_USE_MACH_VOUCHER
1720 #else // DISPATCH_VARIANT_DYLD_STUB
1722 firehose_activity_id_t
1723 voucher_get_activity_id_4dyld(void)
1725 #if VOUCHER_USE_MACH_VOUCHER
1726 return _voucher_get_activity_id(_voucher_get(), NULL
);
1732 #endif // DISPATCH_VARIANT_DYLD_STUB