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
);
92 DISPATCH_ALWAYS_INLINE
93 static inline voucher_t
94 _voucher_clone(const voucher_t ov
, voucher_fields_t ignore_fields
)
96 mach_voucher_attr_recipe_size_t extra
= 0;
99 if (ov
&& !(ignore_fields
& VOUCHER_FIELD_EXTRA
)) {
100 extra
= _voucher_extra_size(ov
);
102 v
= _voucher_alloc(extra
);
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
;
111 if (fields
& VOUCHER_FIELD_PRIORITY
) {
112 v
->v_priority
= ov
->v_priority
;
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
;
119 if ((fields
& VOUCHER_FIELD_EXTRA
) && extra
) {
120 memcpy(_voucher_extra_recipes(v
), _voucher_extra_recipes(ov
),extra
);
127 voucher_adopt(voucher_t voucher
)
129 if (voucher
== VOUCHER_CURRENT
) {
130 return _voucher_copy();
132 return _voucher_adopt(voucher
);
138 return _voucher_copy();
142 voucher_copy_without_importance(void)
144 return _voucher_copy_without_importance();
148 voucher_retain(voucher_t voucher
)
150 return _voucher_retain(voucher
);
154 voucher_release(voucher_t voucher
)
156 return _voucher_release(voucher
);
160 _voucher_thread_cleanup(void *voucher
)
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
);
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)
176 _voucher_find_and_retain(mach_voucher_t kv
)
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");
190 // resurrection: raced with _voucher_remove
191 (void)os_atomic_inc2o(v
, os_obj_ref_cnt
, relaxed
);
196 _vouchers_lock_unlock();
201 _voucher_insert(voucher_t v
)
203 mach_voucher_t kv
= v
->v_ipc_kvoucher
;
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");
210 TAILQ_INSERT_TAIL(_vouchers_head(kv
), v
, v_list
);
211 _vouchers_lock_unlock();
215 _voucher_remove(voucher_t v
)
217 mach_voucher_t kv
= v
->v_ipc_kvoucher
;
218 if (!_TAILQ_IS_ENQUEUED(v
, v_list
)) return;
219 _vouchers_lock_lock();
221 _dispatch_voucher_debug("corruption", v
);
222 DISPATCH_CLIENT_CRASH(0, "Voucher corruption");
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;
231 _vouchers_lock_unlock();
235 _voucher_dealloc_mach_voucher(mach_voucher_t kv
)
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
);
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
)
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
);
258 _dispatch_kvoucher_debug("create", kv
);
259 _dispatch_voucher_debug_machport(kv
);
266 _voucher_task_mach_voucher_init(void* ctxt DISPATCH_UNUSED
)
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
,
275 kr
= _voucher_create_mach_voucher(&task_create_recipe
,
276 sizeof(task_create_recipe
), &kv
);
278 DISPATCH_CLIENT_CRASH(kr
, "Could not create task mach voucher");
280 _voucher_default_task_mach_voucher
= kv
;
282 _voucher_task_mach_voucher
= kv
;
286 voucher_replace_default_voucher(void)
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
) {
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
,
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
;
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
);
310 _dispatch_voucher_debug("kvoucher[0x%08x] replace default voucher", v
, tkv
);
313 #define _voucher_mach_recipe_size(payload_size) \
314 (sizeof(mach_voucher_attr_recipe_data_t) + (payload_size))
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)))
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)))
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
)
334 mach_voucher_attr_recipe_size_t extra
= _voucher_extra_size(v
);
335 mach_voucher_attr_recipe_size_t size
= 0;
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
;
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
,
346 size
+= _voucher_mach_recipe_size(0);
348 #if VOUCHER_USE_MACH_VOUCHER_PRIORITY
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
),
356 mvar_buf
= _dispatch_memappend(mvar_buf
, &value
);
357 size
+= _voucher_mach_recipe_size(sizeof(value
));
359 #endif // VOUCHER_USE_MACH_VOUCHER_PRIORITY
361 if ((v
&& v
->v_activity
) || pp
) {
362 _voucher_mach_udata_s
*udata_buf
;
363 unsigned udata_size
= 0;
365 if (v
&& v
->v_activity
) {
366 udata_size
= offsetof(_voucher_mach_udata_s
, _vmu_after_activity
);
368 udata_size
= offsetof(_voucher_mach_udata_s
, _vmu_after_priority
);
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
,
375 udata_buf
= (_voucher_mach_udata_s
*)(mvar_buf
->content
);
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
,
386 *udata_buf
= (_voucher_mach_udata_s
){
387 .vmu_magic
= VOUCHER_MAGIC_V3
,
388 .vmu_priority
= (_voucher_priority_t
)pp
,
392 mvar_buf
= (mach_voucher_attr_recipe_t
)(mvar_buf
->content
+ udata_size
);
393 size
+= _voucher_mach_recipe_size(udata_size
);
397 memcpy(mvar_buf
, _voucher_extra_recipes(v
), extra
);
404 _voucher_get_mach_voucher(voucher_t voucher
)
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
)) {
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
;
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
;
425 if (!os_atomic_cmpxchgv2o(voucher
, v_ipc_kvoucher
, MACH_VOUCHER_NULL
,
426 kv
, &kvo
, relaxed
)) {
427 _voucher_dealloc_mach_voucher(kv
);
430 if (kv
== voucher
->v_kvoucher
) {
431 // if v_kvoucher == v_ipc_kvoucher we keep only one reference
432 _voucher_dealloc_mach_voucher(kv
);
434 _voucher_insert(voucher
);
435 _dispatch_voucher_debug("kvoucher[0x%08x] create", voucher
, kv
);
441 _voucher_create_mach_voucher_with_priority(voucher_t voucher
,
442 pthread_priority_t priority
)
444 if (priority
== _voucher_get_priority(voucher
)) {
445 return MACH_VOUCHER_NULL
; // caller will use _voucher_get_mach_voucher
448 mach_voucher_t kv
, kvb
= voucher
? voucher
->v_kvoucher
: MACH_VOUCHER_NULL
;
449 if (!kvb
) kvb
= _voucher_get_task_mach_voucher();
451 mach_voucher_attr_recipe_t mvar
= _voucher_mach_recipe_alloca(voucher
);
452 mach_voucher_attr_recipe_size_t size
;
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
;
459 _dispatch_kvoucher_debug("create with priority from voucher[%p]", kv
,
465 _voucher_create_with_mach_voucher(mach_voucher_t kv
, mach_msg_bits_t msgh_bits
)
467 if (!kv
) return NULL
;
469 mach_voucher_attr_recipe_t vr
;
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
476 const mach_voucher_attr_recipe_data_t redeem_recipe
[] = {
478 .key
= MACH_VOUCHER_ATTR_KEY_ALL
,
479 .command
= MACH_VOUCHER_ATTR_COPY
,
480 .previous_voucher
= kv
,
483 .key
= MACH_VOUCHER_ATTR_KEY_BANK
,
484 .command
= MACH_VOUCHER_ATTR_REDEEM
,
487 kr
= _voucher_create_mach_voucher(redeem_recipe
, sizeof(redeem_recipe
),
489 if (!dispatch_assume_zero(kr
)) {
490 _voucher_dealloc_mach_voucher(kv
);
491 _dispatch_kvoucher_debug("redeemed from 0x%08x", rkv
, kv
);
494 _dispatch_voucher_debug_machport(kv
);
497 voucher_t v
= _voucher_find_and_retain(kv
);
499 _dispatch_voucher_debug("kvoucher[0x%08x] found", v
, kv
);
500 _voucher_dealloc_mach_voucher(kv
);
503 vr_size
= sizeof(*vr
) + sizeof(_voucher_mach_udata_s
);
504 vr
= alloca(vr_size
);
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
));
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
);
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
;
527 bool remove_kv_userdata
= false;
528 if (udata_sz
>= offsetof(_voucher_mach_udata_s
, _vmu_after_activity
)) {
530 remove_kv_userdata
= true;
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
;
539 if (remove_kv_userdata
) {
540 mach_voucher_t nkv
= MACH_VOUCHER_NULL
;
541 const mach_voucher_attr_recipe_data_t remove_userdata_recipe
[] = {
543 .key
= MACH_VOUCHER_ATTR_KEY_ALL
,
544 .command
= MACH_VOUCHER_ATTR_COPY
,
545 .previous_voucher
= kv
,
548 .key
= MACH_VOUCHER_ATTR_KEY_USER_DATA
,
549 .command
= MACH_VOUCHER_ATTR_REMOVE
,
551 #if VOUCHER_USE_MACH_VOUCHER_PRIORITY
553 .key
= MACH_VOUCHER_ATTR_KEY_PTHPRIORITY
,
554 .command
= MACH_VOUCHER_ATTR_REMOVE
,
558 mach_voucher_attr_recipe_size_t size
= sizeof(remove_userdata_recipe
);
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
;
566 v
->v_kvbase
= _voucher_find_and_retain(nkv
);
568 _voucher_dealloc_mach_voucher(nkv
); // borrow base reference
570 _voucher_dealloc_mach_voucher(kv
);
573 _dispatch_voucher_debug_machport(kv
);
578 _dispatch_voucher_debug("kvoucher[0x%08x] create", v
, kv
);
583 _voucher_create_with_priority_and_mach_voucher(voucher_t ov
,
584 pthread_priority_t priority
, mach_voucher_t kv
)
586 if (priority
== _voucher_get_priority(ov
)) {
587 if (kv
) _voucher_dealloc_mach_voucher(kv
);
588 return ov
? _voucher_retain(ov
) : NULL
;
590 voucher_t v
= _voucher_find_and_retain(kv
);
591 voucher_fields_t ignore_fields
= VOUCHER_FIELD_PRIORITY
;
594 _dispatch_voucher_debug("kvoucher[0x%08x] find", v
, kv
);
595 _voucher_dealloc_mach_voucher(kv
);
599 if (kv
) ignore_fields
|= VOUCHER_FIELD_KVOUCHER
;
600 v
= _voucher_clone(ov
, ignore_fields
);
602 v
->v_priority
= (_voucher_priority_t
)priority
;
605 v
->v_ipc_kvoucher
= v
->v_kvoucher
= kv
;
607 _dispatch_voucher_debug("kvoucher[0x%08x] create with priority from "
608 "voucher[%p]", v
, kv
, ov
);
609 _dispatch_voucher_debug_machport(kv
);
615 _voucher_create_without_importance(voucher_t ov
)
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
);
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
[] = {
627 .key
= MACH_VOUCHER_ATTR_KEY_ALL
,
628 .command
= MACH_VOUCHER_ATTR_COPY
,
629 .previous_voucher
= okv
,
632 .key
= MACH_VOUCHER_ATTR_KEY_IMPORTANCE
,
633 .command
= MACH_VOUCHER_ATTR_REMOVE
,
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
;
643 _voucher_dealloc_mach_voucher(kv
);
644 return _voucher_retain(ov
);
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
);
653 voucher_t kvbase
= v
;
654 voucher_fields_t ignore_fields
= VOUCHER_FIELD_KVOUCHER
;
655 v
= _voucher_clone(ov
, ignore_fields
);
657 if (ov
->v_ipc_kvoucher
) {
658 v
->v_ipc_kvoucher
= kv
;
661 v
->v_kvbase
= kvbase
;
662 _voucher_dealloc_mach_voucher(kv
); // borrow base reference
665 _dispatch_voucher_debug("kvoucher[0x%08x] create without importance "
666 "from voucher[%p]", v
, kv
, ov
);
672 _voucher_create_accounting_voucher(voucher_t ov
)
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
,
685 kr
= _voucher_create_mach_voucher(&accounting_copy_recipe
,
686 sizeof(accounting_copy_recipe
), &kv
);
687 if (dispatch_assume_zero(kr
) || !kv
){
690 voucher_t v
= _voucher_find_and_retain(kv
);
692 _dispatch_voucher_debug("kvoucher[0x%08x] find accounting voucher "
693 "from voucher[%p]", v
, kv
, ov
);
694 _voucher_dealloc_mach_voucher(kv
);
697 v
= _voucher_alloc(0);
698 v
->v_ipc_kvoucher
= v
->v_kvoucher
= kv
;
700 v
->v_kvbase
= _voucher_retain(ov
);
701 _voucher_dealloc_mach_voucher(kv
); // borrow base reference
704 _dispatch_voucher_debug("kvoucher[0x%08x] create accounting voucher "
705 "from voucher[%p]", v
, kv
, ov
);
710 voucher_create_with_mach_msg(mach_msg_header_t
*msg
)
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
);
718 voucher_decrement_importance_count4CF(voucher_t v
)
720 if (!v
|| !v
->v_kvoucher
|| !v
->v_kv_has_importance
) return;
722 mach_voucher_t kv
= v
->v_ipc_kvoucher
? v
->v_ipc_kvoucher
: v
->v_kvoucher
;
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;
729 uint32_t count
= UINT32_MAX
;
730 kvc_out
= (mach_voucher_attr_content_t
)&count
;
731 kvc_out_size
= sizeof(count
);
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
739 _dispatch_voucher_debug("kvoucher[0x%08x] decrement importance count to %u:"
740 " %s - 0x%x", v
, kv
, count
, mach_error_string(kr
), kr
);
742 if (slowpath(dispatch_assume_zero(kr
) == KERN_FAILURE
)) {
743 DISPATCH_CLIENT_CRASH(kr
, "Voucher importance count underflow");
747 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
749 voucher_get_mach_voucher(voucher_t voucher
)
751 return _voucher_get_mach_voucher(voucher
);
756 _voucher_xref_dispose(voucher_t voucher
)
758 _dispatch_voucher_debug("xref_dispose", voucher
);
759 _voucher_remove(voucher
);
760 return _os_object_release_internal_inline((_os_object_t
)voucher
);
764 _voucher_dispose(voucher_t voucher
)
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");
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
);
776 voucher
->v_ipc_kvoucher
= MACH_VOUCHER_NULL
;
778 if (voucher
->v_kvoucher
) {
779 if (!voucher
->v_kvbase
) {
780 _voucher_dealloc_mach_voucher(voucher
->v_kvoucher
);
782 voucher
->v_kvoucher
= MACH_VOUCHER_NULL
;
784 if (voucher
->v_kvbase
) {
785 _voucher_release(voucher
->v_kvbase
);
786 voucher
->v_kvbase
= NULL
;
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;
796 return _os_object_dealloc((_os_object_t
)voucher
);
800 _voucher_activity_debug_channel_barrier_nop(void *ctxt DISPATCH_UNUSED
)
805 _voucher_activity_debug_channel_init(void)
807 dispatch_mach_handler_function_t handler
= NULL
;
809 if (_voucher_libtrace_hooks
&& _voucher_libtrace_hooks
->vah_version
>= 2) {
810 handler
= _voucher_libtrace_hooks
->vah_debug_channel_handler
;
813 if (!handler
) return;
819 kr
= task_get_debug_control_port(mach_task_self(), &dbgp
);
820 DISPATCH_VERIFY_MIG(kr
);
822 DISPATCH_CLIENT_CRASH(kr
, "Couldn't get debug control port");
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
;
836 _voucher_atfork_child(void)
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
;
844 _voucher_aid_next
= 0;
845 _firehose_task_buffer_pred
= 0;
846 _firehose_task_buffer
= NULL
; // firehose buffer is VM_INHERIT_NONE
849 #if VOUCHER_EXPORT_PERSONA_SPI
850 #if VOUCHER_USE_PERSONA
852 _voucher_get_current_persona_token(struct persona_token
*token
)
854 kern_return_t kr
= KERN_FAILURE
;
855 voucher_t v
= _voucher_get();
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
);
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
);
881 voucher_get_current_persona(void)
883 uid_t persona_id
= PERSONA_ID_NONE
;
885 #if VOUCHER_USE_PERSONA
886 struct persona_token token
;
889 if (_voucher_get_current_persona_token(&token
) == KERN_SUCCESS
) {
890 return token
.originator
.persona_id
;
893 // falling back to the process persona if there is no adopted voucher
894 if (kpersona_get(&persona_id
) < 0) {
897 (void)dispatch_assume_zero(err
);
905 voucher_get_current_persona_originator_info(struct proc_persona_info
*persona_info
)
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
;
920 voucher_get_current_persona_proximate_info(struct proc_persona_info
*persona_info
)
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
;
936 #pragma mark _voucher_init
939 voucher_mach_msg_set(mach_msg_header_t
*msg
)
941 return _voucher_mach_msg_set(msg
, _voucher_get());
945 voucher_mach_msg_clear(mach_msg_header_t
*msg
)
947 (void)_voucher_mach_msg_clear(msg
, false);
950 voucher_mach_msg_state_t
951 voucher_mach_msg_adopt(mach_msg_header_t
*msg
)
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
);
961 voucher_mach_msg_revert(voucher_mach_msg_state_t state
)
963 if (state
== VOUCHER_MACH_MSG_STATE_UNCHANGED
) return;
964 _voucher_replace((voucher_t
)state
);
967 #if DISPATCH_USE_LIBKERNEL_VOUCHER_INIT
968 #include <_libkernel_init.h>
970 static const struct _libkernel_voucher_functions _voucher_libkernel_functions
=
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
,
980 _voucher_libkernel_init(void)
982 kern_return_t kr
= __libkernel_voucher_init(&_voucher_libkernel_functions
);
983 dispatch_assert(!kr
);
986 #define _voucher_libkernel_init()
990 voucher_activity_initialize_4libtrace(voucher_activity_hooks_t hooks
)
992 if (!os_atomic_cmpxchg(&_voucher_libtrace_hooks
, NULL
,
994 DISPATCH_CLIENT_CRASH(_voucher_libtrace_hooks
,
995 "voucher_activity_initialize_4libtrace called twice");
1002 _voucher_libkernel_init();
1004 for (i
= 0; i
< VL_HASH_SIZE
; i
++) {
1005 TAILQ_INIT(&_vouchers
[i
]);
1010 #pragma mark voucher_activity_t
1014 _voucher_activity_id_allocate_slow(uint64_t aid
)
1019 kr
= mach_generate_activity_id(mach_task_self(), 1, &next
);
1021 DISPATCH_CLIENT_CRASH(kr
, "Could not generate an activity ID");
1023 next
*= MACH_ACTIVITY_ID_RANGE_SIZE
;
1024 next
&= MACH_ACTIVITY_ID_MASK
;
1025 if (unlikely(next
== 0)) {
1029 if (unlikely(aid
== 0)) {
1030 if (os_atomic_cmpxchg(&_voucher_aid_next
, 0, next
+ 1, relaxed
)) {
1034 return os_atomic_xchg(&_voucher_aid_next
, next
, relaxed
);
1037 DISPATCH_ALWAYS_INLINE
1038 static firehose_activity_id_t
1039 _voucher_activity_id_allocate(firehose_activity_flags_t flags
)
1042 os_atomic_rmw_loop(&_voucher_aid_next
, aid
, next
, relaxed
, {
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
);
1051 return FIREHOSE_ACTIVITY_ID_MAKE(aid
, flags
);
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)
1058 #define _voucher_activity_tracepoint_flush(ft, ftid) \
1059 firehose_buffer_tracepoint_flush(_firehose_task_buffer, ft, ftid)
1063 _firehose_task_buffer_init(void *ctx OS_UNUSED
)
1065 mach_port_t logd_port
;
1067 /* Query the uniquepid of the current process */
1068 struct proc_uniqidentifierinfo p_uniqinfo
= { };
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");
1076 _voucher_unique_pid
= p_uniqinfo
.p_uniqueid
;
1079 if (!fastpath(_voucher_libtrace_hooks
)) {
1080 if (0) { // <rdar://problem/23393959>
1081 DISPATCH_CLIENT_CRASH(0,
1082 "Activity subsystem isn't initialized yet");
1086 logd_port
= _voucher_libtrace_hooks
->vah_get_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
;
1094 // firehose_buffer_create always consumes the send-right
1095 _firehose_task_buffer
= firehose_buffer_create(logd_port
,
1096 _voucher_unique_pid
, flags
);
1100 DISPATCH_ALWAYS_INLINE
1102 _voucher_activity_disabled(void)
1104 dispatch_once_f(&_firehose_task_buffer_pred
,
1105 NULL
, _firehose_task_buffer_init
);
1107 firehose_buffer_t fb
= _firehose_task_buffer
;
1109 return slowpath(fb
->fb_header
.fbh_sendp
== MACH_PORT_DEAD
);
1115 voucher_activity_get_metadata_buffer(size_t *length
)
1117 if (_voucher_activity_disabled()) {
1122 firehose_buffer_header_t fbh
= &_firehose_task_buffer
->fb_header
;
1124 *length
= FIREHOSE_BUFFER_LIBTRACE_HEADER_SIZE
;
1125 return (void *)((uintptr_t)(fbh
+ 1) - *length
);
1129 voucher_activity_create(firehose_tracepoint_id_t trace_id
,
1130 voucher_t base
, firehose_activity_flags_t flags
, uint64_t location
)
1132 return voucher_activity_create_with_location(&trace_id
, base
, flags
, location
);
1136 voucher_activity_create_with_location(firehose_tracepoint_id_t
*trace_id
,
1137 voucher_t base
, firehose_activity_flags_t flags
, uint64_t location
)
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();
1146 if (base
== VOUCHER_CURRENT
) {
1149 if (_voucher_activity_disabled()) {
1151 return base
? _voucher_retain(base
) : VOUCHER_NULL
;
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);
1163 if (base
!= VOUCHER_NULL
) {
1164 parent_id
= base
->v_activity
;
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
);
1173 if (firehose_precise_timestamps_enabled()) {
1174 flags
|= firehose_activity_flags_precise_timestamp
;
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
;
1182 static const firehose_stream_t streams
[2] = {
1183 firehose_stream_metadata
,
1184 firehose_stream_persist
,
1186 firehose_tracepoint_t ft
;
1187 uint64_t stamp
= firehose_tracepoint_time(flags
);
1189 for (size_t i
= 0; i
< countof(streams
); i
++) {
1190 ft
= _voucher_activity_tracepoint_reserve(stamp
, streams
[i
], pubsize
,
1192 if (!fastpath(ft
)) continue;
1194 uint8_t *pubptr
= ft
->ft_data
;
1196 pubptr
= _dispatch_memappend(pubptr
, ¤t_id
);
1199 pubptr
= _dispatch_memappend(pubptr
, &creator_id
);
1202 pubptr
= _dispatch_memappend(pubptr
, &parent_id
);
1204 pubptr
= _dispatch_memappend(pubptr
, &va_id
);
1205 pubptr
= _dispatch_memappend(pubptr
, &location
);
1206 _voucher_activity_tracepoint_flush(ft
, ftid
);
1208 *trace_id
= ftid
.ftid_value
;
1213 _voucher_activity_swap(firehose_activity_id_t old_id
,
1214 firehose_activity_id_t new_id
)
1216 if (_voucher_activity_disabled()) return;
1218 firehose_tracepoint_id_u ftid
= { .ftid
= {
1219 ._namespace
= firehose_tracepoint_namespace_activity
,
1220 ._type
= _firehose_tracepoint_type_activity_swap
,
1222 uint16_t pubsize
= 0;
1225 FIREHOSE_TRACE_ID_SET_FLAG(ftid
, base
, has_current_aid
);
1226 pubsize
+= sizeof(firehose_activity_id_t
);
1229 FIREHOSE_TRACE_ID_SET_FLAG(ftid
, activity
, has_other_aid
);
1230 pubsize
+= sizeof(firehose_activity_id_t
);
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
);
1239 _dispatch_voucher_ktrace_activity_adopt(new_id
);
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
);
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
)
1253 if (v
== VOUCHER_CURRENT
) {
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
;
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
;
1266 firehose_activity_id_t
1267 voucher_get_activity_id(voucher_t v
, firehose_activity_id_t
*parent_id
)
1269 return voucher_get_activity_id_and_creator(v
, NULL
, parent_id
);
1273 voucher_activity_flush(firehose_stream_t stream
)
1275 if (_voucher_activity_disabled()) return;
1276 firehose_buffer_stream_flush(_firehose_task_buffer
, stream
);
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
)
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
);
1290 if (_voucher_activity_disabled()) return 0;
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
;
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
);
1304 if (FIREHOSE_TRACE_ID_HAS_FLAG(ftid
, base
, has_unique_pid
)) {
1306 pubsize
+= sizeof(creator_pid
);
1308 FIREHOSE_TRACE_ID_CLEAR_FLAG(ftid
, base
, has_unique_pid
);
1315 FIREHOSE_TRACE_ID_SET_FLAG(ftid
, log
, has_private_data
);
1316 pubsize
+= sizeof(struct firehose_buffer_range_s
);
1319 if (slowpath(ft_size
+ pubsize
+ privlen
> _firehose_chunk_payload_size
)) {
1320 DISPATCH_CLIENT_CRASH(ft_size
+ pubsize
+ privlen
, "Log is too large");
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
;
1328 pubptr
= _dispatch_memappend(pubptr
, &va_id
);
1331 pubptr
= _dispatch_memappend(pubptr
, &creator_pid
);
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
,
1339 pubptr
= _dispatch_memappend(pubptr
, &range
);
1340 _dispatch_mempcpy(privptr
, privdata
, privlen
);
1342 _dispatch_mempcpy(pubptr
, pubdata
, publen
);
1343 _voucher_activity_tracepoint_flush(ft
, ftid
);
1344 return ftid
.ftid_value
;
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
)
1352 firehose_tracepoint_id_u ftid
= { .ftid_value
= trace_id
};
1353 return _voucher_activity_trace(stream
, ftid
, timestamp
, pubdata
, publen
,
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
)
1363 firehose_tracepoint_id_u ftid
= { .ftid_value
= trace_id
};
1364 return _voucher_activity_trace(stream
, ftid
, timestamp
,
1365 pubdata
, publen
, privdata
, privlen
);
1369 #pragma mark _voucher_debug
1372 _voucher_debug(voucher_t v
, char* buf
, size_t bufsiz
)
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);
1381 bufprintf(", base voucher %p", v
->v_kvbase
);
1383 if (v
->v_kvoucher
) {
1384 bufprintf(", kvoucher%s 0x%x", v
->v_kvoucher
== v
->v_ipc_kvoucher
?
1385 " & ipc kvoucher" : "", v
->v_kvoucher
);
1387 if (v
->v_ipc_kvoucher
&& v
->v_ipc_kvoucher
!= v
->v_kvoucher
) {
1388 bufprintf(", ipc kvoucher 0x%x", v
->v_ipc_kvoucher
);
1390 if (v
->v_priority
) {
1391 bufprintf(", QOS 0x%x", v
->v_priority
);
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
);
1401 #else // VOUCHER_USE_MACH_VOUCHER
1404 #pragma mark Simulator / vouchers disabled
1406 #if VOUCHER_ENABLE_RECIPE_OBJECTS
1408 voucher_create(voucher_recipe_t recipe
)
1416 voucher_adopt(voucher_t voucher
)
1428 voucher_copy_without_importance(void)
1434 voucher_retain(voucher_t voucher
)
1440 voucher_release(voucher_t voucher
)
1446 voucher_replace_default_voucher(void)
1451 voucher_decrement_importance_count4CF(voucher_t v
)
1457 _voucher_thread_cleanup(void *voucher
)
1463 _voucher_dealloc_mach_voucher(mach_voucher_t kv
)
1469 _voucher_get_mach_voucher(voucher_t voucher
)
1472 return MACH_VOUCHER_NULL
;
1476 _voucher_create_mach_voucher_with_priority(voucher_t voucher
,
1477 pthread_priority_t priority
)
1479 (void)voucher
; (void)priority
;
1480 return MACH_VOUCHER_NULL
;
1484 _voucher_create_with_priority_and_mach_voucher(voucher_t voucher
,
1485 pthread_priority_t priority
, mach_voucher_t kv
)
1487 (void)voucher
; (void)priority
; (void)kv
;
1492 _voucher_create_accounting_voucher(voucher_t voucher
)
1499 voucher_create_with_mach_msg(mach_msg_header_t
*msg
)
1505 #if VOUCHER_ENABLE_GET_MACH_VOUCHER
1507 voucher_get_mach_voucher(voucher_t voucher
)
1515 _voucher_xref_dispose(voucher_t voucher
)
1521 _voucher_dispose(voucher_t voucher
)
1526 #if VOUCHER_EXPORT_PERSONA_SPI
1528 voucher_get_current_persona(void)
1530 return PERSONA_ID_NONE
;
1534 voucher_get_current_persona_originator_info(struct proc_persona_info
*persona_info
)
1541 voucher_get_current_persona_proximate_info(struct proc_persona_info
*persona_info
)
1549 _voucher_activity_debug_channel_init(void)
1554 _voucher_atfork_child(void)
1564 voucher_activity_get_metadata_buffer(size_t *length
)
1571 voucher_activity_create(firehose_tracepoint_id_t trace_id
,
1572 voucher_t base
, firehose_activity_flags_t flags
, uint64_t location
)
1574 (void)trace_id
; (void)base
; (void)flags
; (void)location
;
1579 voucher_activity_create_with_location(firehose_tracepoint_id_t
*trace_id
,
1580 voucher_t base
, firehose_activity_flags_t flags
, uint64_t location
)
1582 (void)trace_id
; (void)base
; (void)flags
; (void)location
;
1586 firehose_activity_id_t
1587 voucher_get_activity_id(voucher_t voucher
, firehose_activity_id_t
*parent_id
)
1589 (void)voucher
; (void)parent_id
;
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
)
1597 if (creator_pid
) *creator_pid
= 0;
1598 (void)voucher
; (void)parent_id
;
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
)
1607 (void)stream
; (void)trace_id
; (void)timestamp
;
1608 (void)pubdata
; (void)publen
;
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
)
1618 (void)stream
; (void)trace_id
; (void)timestamp
;
1619 (void)pubdata
; (void)publen
; (void)privdata
; (void)privlen
;
1624 voucher_activity_flush(firehose_stream_t stream
)
1630 voucher_activity_initialize_4libtrace(voucher_activity_hooks_t hooks
)
1636 _voucher_debug(voucher_t v
, char* buf
, size_t bufsiz
)
1638 (void)v
; (void)buf
; (void)bufsiz
;
1642 #endif // VOUCHER_USE_MACH_VOUCHER
1644 #else // DISPATCH_VARIANT_DYLD_STUB
1646 firehose_activity_id_t
1647 voucher_get_activity_id_4dyld(void)
1649 #if VOUCHER_USE_MACH_VOUCHER
1650 return _voucher_get_activity_id(_voucher_get(), NULL
);
1656 #endif // DISPATCH_VARIANT_DYLD_STUB