2 * Copyright (c) 2013 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <mach/mach_types.h>
30 #include <mach/mach_traps.h>
31 #include <mach/notify.h>
32 #include <ipc/ipc_types.h>
33 #include <ipc/ipc_port.h>
34 #include <ipc/ipc_voucher.h>
35 #include <kern/ipc_kobject.h>
36 #include <kern/ipc_tt.h>
37 #include <kern/mach_param.h>
38 #include <kern/kalloc.h>
39 #include <kern/zalloc.h>
41 #include <libkern/OSAtomic.h>
43 #include <mach/mach_voucher_server.h>
44 #include <mach/mach_voucher_attr_control_server.h>
45 #include <mach/mach_host_server.h>
46 #include <voucher/ipc_pthread_priority_types.h>
49 * Sysctl variable; enable and disable tracing of voucher contents
51 uint32_t ipc_voucher_trace_contents
= 0;
53 static zone_t ipc_voucher_zone
;
54 static zone_t ipc_voucher_attr_control_zone
;
59 #define IV_HASH_BUCKETS 127
60 #define IV_HASH_BUCKET(x) ((x) % IV_HASH_BUCKETS)
62 static queue_head_t ivht_bucket
[IV_HASH_BUCKETS
];
63 static lck_spin_t ivht_lock_data
;
64 static uint32_t ivht_count
= 0;
66 #define ivht_lock_init() \
67 lck_spin_init(&ivht_lock_data, &ipc_lck_grp, &ipc_lck_attr)
68 #define ivht_lock_destroy() \
69 lck_spin_destroy(&ivht_lock_data, &ipc_lck_grp)
71 lck_spin_lock_grp(&ivht_lock_data, &ipc_lck_grp)
72 #define ivht_lock_try() \
73 lck_spin_try_lock_grp(&ivht_lock_data, &ipc_lck_grp)
74 #define ivht_unlock() \
75 lck_spin_unlock(&ivht_lock_data)
78 * Global table of resource manager registrations
80 * NOTE: For now, limited to well-known resource managers
81 * eventually, will include dynamic allocations requiring
82 * table growth and hashing by key.
84 static iv_index_t ivgt_keys_in_use
= MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
;
85 static ipc_voucher_global_table_element iv_global_table
[MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
];
86 static lck_spin_t ivgt_lock_data
;
88 #define ivgt_lock_init() \
89 lck_spin_init(&ivgt_lock_data, &ipc_lck_grp, &ipc_lck_attr)
90 #define ivgt_lock_destroy() \
91 lck_spin_destroy(&ivgt_lock_data, &ipc_lck_grp)
93 lck_spin_lock_grp(&ivgt_lock_data, &ipc_lck_grp)
94 #define ivgt_lock_try() \
95 lck_spin_try_lock_grp(&ivgt_lock_data, &ipc_lck_grp)
96 #define ivgt_unlock() \
97 lck_spin_unlock(&ivgt_lock_data)
99 ipc_voucher_t
iv_alloc(iv_index_t entries
);
100 void iv_dealloc(ipc_voucher_t iv
, boolean_t unhash
);
102 os_refgrp_decl(static, iv_refgrp
, "voucher", NULL
);
103 os_refgrp_decl(static, ivac_refgrp
, "voucher attribute control", NULL
);
106 iv_reference(ipc_voucher_t iv
)
108 os_ref_retain(&iv
->iv_refs
);
112 iv_release(ipc_voucher_t iv
)
114 if (os_ref_release(&iv
->iv_refs
) == 0) {
115 iv_dealloc(iv
, TRUE
);
120 * freelist helper macros
122 #define IV_FREELIST_END ((iv_index_t) 0)
125 * Attribute value hashing helper macros
127 #define IV_HASH_END UINT32_MAX
128 #define IV_HASH_VAL(sz, val) \
129 (((val) >> 3) % (sz))
131 static inline iv_index_t
133 iv_index_t key_index
,
134 mach_voucher_attr_value_handle_t value
)
136 ipc_voucher_attr_control_t ivac
;
138 ivac
= iv_global_table
[key_index
].ivgte_control
;
139 assert(IVAC_NULL
!= ivac
);
140 return IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
144 * Convert a key to an index. This key-index is used to both index
145 * into the voucher table of attribute cache indexes and also the
146 * table of resource managers by key.
148 * For now, well-known keys have a one-to-one mapping of indexes
149 * into these tables. But as time goes on, that may not always
150 * be the case (sparse use over time). This isolates the code from
151 * having to change in these cases - yet still lets us keep a densely
152 * packed set of tables.
154 static inline iv_index_t
155 iv_key_to_index(mach_voucher_attr_key_t key
)
157 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
||
158 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
< key
) {
159 return IV_UNUSED_KEYINDEX
;
161 return (iv_index_t
)key
- 1;
164 static inline mach_voucher_attr_key_t
165 iv_index_to_key(iv_index_t key_index
)
167 if (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
> key_index
) {
168 return iv_global_table
[key_index
].ivgte_key
;
170 return MACH_VOUCHER_ATTR_KEY_NONE
;
173 static void ivace_release(iv_index_t key_index
, iv_index_t value_index
);
174 static void ivace_lookup_values(iv_index_t key_index
, iv_index_t value_index
,
175 mach_voucher_attr_value_handle_array_t values
,
176 mach_voucher_attr_value_handle_array_size_t
*count
);
178 static iv_index_t
iv_lookup(ipc_voucher_t
, iv_index_t
);
181 static void ivgt_lookup(iv_index_t
,
183 ipc_voucher_attr_manager_t
*,
184 ipc_voucher_attr_control_t
*);
187 ipc_voucher_prepare_processing_recipe(
188 ipc_voucher_t voucher
,
189 ipc_voucher_attr_raw_recipe_array_t recipes
,
190 ipc_voucher_attr_raw_recipe_array_size_t
*in_out_size
,
191 mach_voucher_attr_recipe_command_t command
,
192 ipc_voucher_attr_manager_flags flags
,
193 int *need_processing
);
195 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
196 void user_data_attr_manager_init(void);
200 ipc_voucher_init(void)
202 natural_t ipc_voucher_max
= (task_max
+ thread_max
) * 2;
203 natural_t attr_manager_max
= MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
;
206 ipc_voucher_zone
= zinit(sizeof(struct ipc_voucher
),
207 ipc_voucher_max
* sizeof(struct ipc_voucher
),
208 sizeof(struct ipc_voucher
),
210 zone_change(ipc_voucher_zone
, Z_NOENCRYPT
, TRUE
);
212 ipc_voucher_attr_control_zone
= zinit(sizeof(struct ipc_voucher_attr_control
),
213 attr_manager_max
* sizeof(struct ipc_voucher_attr_control
),
214 sizeof(struct ipc_voucher_attr_control
),
215 "ipc voucher attr controls");
216 zone_change(ipc_voucher_attr_control_zone
, Z_NOENCRYPT
, TRUE
);
218 /* initialize voucher hash */
220 for (i
= 0; i
< IV_HASH_BUCKETS
; i
++) {
221 queue_init(&ivht_bucket
[i
]);
224 /* initialize global table locking */
227 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
228 user_data_attr_manager_init();
233 iv_alloc(iv_index_t entries
)
239 iv
= (ipc_voucher_t
)zalloc(ipc_voucher_zone
);
244 os_ref_init(&iv
->iv_refs
, &iv_refgrp
);
247 iv
->iv_port
= IP_NULL
;
249 if (entries
> IV_ENTRIES_INLINE
) {
252 /* TODO - switch to ipc_table method of allocation */
253 table
= (iv_entry_t
) kalloc(sizeof(*table
) * entries
);
254 if (IVE_NULL
== table
) {
255 zfree(ipc_voucher_zone
, iv
);
258 iv
->iv_table
= table
;
259 iv
->iv_table_size
= entries
;
261 iv
->iv_table
= iv
->iv_inline_table
;
262 iv
->iv_table_size
= IV_ENTRIES_INLINE
;
265 /* initialize the table entries */
266 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
267 iv
->iv_table
[i
] = IV_UNUSED_VALINDEX
;
276 * Set the voucher's value index for a given key index.
278 * This is only called during voucher creation, as
279 * they are immutable once references are distributed.
282 iv_set(ipc_voucher_t iv
,
283 iv_index_t key_index
,
284 iv_index_t value_index
)
286 assert(key_index
< iv
->iv_table_size
);
287 iv
->iv_table
[key_index
] = value_index
;
291 iv_dealloc(ipc_voucher_t iv
, boolean_t unhash
)
293 ipc_port_t port
= iv
->iv_port
;
297 * Do we have to remove it from the hash?
301 assert(os_ref_get_count(&iv
->iv_refs
) == 0);
302 assert(IV_HASH_BUCKETS
> iv
->iv_hash
);
303 queue_remove(&ivht_bucket
[iv
->iv_hash
], iv
, ipc_voucher_t
, iv_hash_link
);
307 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_DESTROY
) | DBG_FUNC_NONE
,
308 VM_KERNEL_ADDRPERM((uintptr_t)iv
), 0, ivht_count
, 0, 0);
310 os_ref_count_t cnt __assert_only
= os_ref_release(&iv
->iv_refs
);
315 * if a port was allocated for this voucher,
316 * it must not have any remaining send rights,
317 * because the port's reference on the voucher
318 * is gone. We can just discard it now.
320 if (IP_VALID(port
)) {
321 assert(ip_active(port
));
322 assert(port
->ip_srights
== 0);
324 ipc_port_dealloc_kernel(port
);
327 /* release the attribute references held by this voucher */
328 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
329 ivace_release(i
, iv
->iv_table
[i
]);
335 if (iv
->iv_table
!= iv
->iv_inline_table
) {
337 iv
->iv_table_size
* sizeof(*iv
->iv_table
));
340 zfree(ipc_voucher_zone
, iv
);
346 * Find the voucher's value index for a given key_index
348 * Vouchers are immutable, so no locking required to do
351 static inline iv_index_t
352 iv_lookup(ipc_voucher_t iv
, iv_index_t key_index
)
354 if (key_index
< iv
->iv_table_size
) {
355 return iv
->iv_table
[key_index
];
357 return IV_UNUSED_VALINDEX
;
361 * Routine: unsafe_convert_port_to_voucher
363 * Unsafe conversion of a port to a voucher.
364 * Intended only for use by trace and debugging
365 * code. Consumes nothing, validates very little,
366 * produces an unreferenced voucher, which you
367 * MAY NOT use as a voucher, only log as an
370 * Caller has a send-right reference to port.
371 * Port may or may not be locked.
374 unsafe_convert_port_to_voucher(
377 if (IP_VALID(port
)) {
378 uintptr_t voucher
= (uintptr_t) port
->ip_kobject
;
381 * No need to lock because we have a reference on the
382 * port, and if it is a true voucher port, that reference
383 * keeps the voucher bound to the port (and active).
385 if (ip_kotype(port
) == IKOT_VOUCHER
) {
389 return (uintptr_t)IV_NULL
;
393 * Routine: convert_port_to_voucher
395 * Convert from a port to a voucher.
396 * Doesn't consume the port [send-right] ref;
397 * produces a voucher ref, which may be null.
399 * Caller has a send-right reference to port.
400 * Port may or may not be locked.
403 convert_port_to_voucher(
406 if (IP_VALID(port
)) {
407 ipc_voucher_t voucher
= (ipc_voucher_t
) port
->ip_kobject
;
410 * No need to lock because we have a reference on the
411 * port, and if it is a true voucher port, that reference
412 * keeps the voucher bound to the port (and active).
414 if (ip_kotype(port
) != IKOT_VOUCHER
) {
418 assert(ip_active(port
));
420 ipc_voucher_reference(voucher
);
427 * Routine: convert_port_name_to_voucher
429 * Convert from a port name in the current space to a voucher.
430 * Produces a voucher ref, which may be null.
436 convert_port_name_to_voucher(
437 mach_port_name_t voucher_name
)
443 if (MACH_PORT_VALID(voucher_name
)) {
444 kr
= ipc_port_translate_send(current_space(), voucher_name
, &port
);
445 if (KERN_SUCCESS
!= kr
) {
449 iv
= convert_port_to_voucher(port
);
458 ipc_voucher_reference(ipc_voucher_t voucher
)
460 if (IPC_VOUCHER_NULL
== voucher
) {
464 iv_reference(voucher
);
468 ipc_voucher_release(ipc_voucher_t voucher
)
470 if (IPC_VOUCHER_NULL
!= voucher
) {
476 * Routine: ipc_voucher_notify
478 * Called whenever the Mach port system detects no-senders
479 * on the voucher port.
481 * Each time the send-right count goes positive, a no-senders
482 * notification is armed (and a voucher reference is donated).
483 * So, each notification that comes in must release a voucher
484 * reference. If more send rights have been added since it
485 * fired (asynchronously), they will be protected by a different
489 ipc_voucher_notify(mach_msg_header_t
*msg
)
491 mach_no_senders_notification_t
*notification
= (void *)msg
;
492 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
495 assert(ip_active(port
));
496 assert(IKOT_VOUCHER
== ip_kotype(port
));
497 iv
= (ipc_voucher_t
)port
->ip_kobject
;
499 ipc_voucher_release(iv
);
503 * Convert a voucher to a port.
506 convert_voucher_to_port(ipc_voucher_t voucher
)
508 ipc_port_t port
, send
;
510 if (IV_NULL
== voucher
) {
514 assert(os_ref_get_count(&voucher
->iv_refs
) > 0);
516 /* create a port if needed */
517 port
= voucher
->iv_port
;
518 if (!IP_VALID(port
)) {
519 port
= ipc_port_alloc_kernel();
520 assert(IP_VALID(port
));
521 ipc_kobject_set_atomically(port
, (ipc_kobject_t
) voucher
, IKOT_VOUCHER
);
523 /* If we lose the race, deallocate and pick up the other guy's port */
524 if (!OSCompareAndSwapPtr(IP_NULL
, port
, &voucher
->iv_port
)) {
525 ipc_port_dealloc_kernel(port
);
526 port
= voucher
->iv_port
;
527 assert(ip_kotype(port
) == IKOT_VOUCHER
);
528 assert(port
->ip_kobject
== (ipc_kobject_t
)voucher
);
533 assert(ip_active(port
));
534 send
= ipc_port_make_send_locked(port
);
536 if (1 == port
->ip_srights
) {
537 ipc_port_t old_notify
;
539 /* transfer our ref to the port, and arm the no-senders notification */
540 assert(IP_NULL
== port
->ip_nsrequest
);
541 ipc_port_nsrequest(port
, port
->ip_mscount
, ipc_port_make_sonce_locked(port
), &old_notify
);
543 assert(IP_NULL
== old_notify
);
545 /* piggyback on the existing port reference, so consume ours */
547 ipc_voucher_release(voucher
);
552 #define ivace_reset_data(ivace_elem, next_index) { \
553 (ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE; \
554 (ivace_elem)->ivace_refs = 0; \
555 (ivace_elem)->ivace_persist = 0; \
556 (ivace_elem)->ivace_made = 0; \
557 (ivace_elem)->ivace_free = TRUE; \
558 (ivace_elem)->ivace_releasing = FALSE; \
559 (ivace_elem)->ivace_layered = 0; \
560 (ivace_elem)->ivace_index = IV_HASH_END; \
561 (ivace_elem)->ivace_next = (next_index); \
564 #define ivace_copy_data(ivace_src_elem, ivace_dst_elem) { \
565 (ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
566 (ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs; \
567 (ivace_dst_elem)->ivace_persist = (ivace_src_elem)->ivace_persist; \
568 (ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made; \
569 (ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free; \
570 (ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered; \
571 (ivace_dst_elem)->ivace_releasing = (ivace_src_elem)->ivace_releasing; \
572 (ivace_dst_elem)->ivace_index = (ivace_src_elem)->ivace_index; \
573 (ivace_dst_elem)->ivace_next = (ivace_src_elem)->ivace_next; \
576 ipc_voucher_attr_control_t
577 ivac_alloc(iv_index_t key_index
)
579 ipc_voucher_attr_control_t ivac
;
584 ivac
= (ipc_voucher_attr_control_t
)zalloc(ipc_voucher_attr_control_zone
);
585 if (IVAC_NULL
== ivac
) {
589 os_ref_init(&ivac
->ivac_refs
, &ivac_refgrp
);
590 ivac
->ivac_is_growing
= FALSE
;
591 ivac
->ivac_port
= IP_NULL
;
593 /* start with just the inline table */
594 table
= (ivac_entry_t
) kalloc(IVAC_ENTRIES_MIN
* sizeof(ivac_entry
));
595 ivac
->ivac_table
= table
;
596 ivac
->ivac_table_size
= IVAC_ENTRIES_MIN
;
597 ivac
->ivac_init_table_size
= IVAC_ENTRIES_MIN
;
598 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
599 ivace_reset_data(&table
[i
], i
+ 1);
602 /* the default table entry is never on freelist */
603 table
[0].ivace_next
= IV_HASH_END
;
604 table
[0].ivace_free
= FALSE
;
605 table
[i
- 1].ivace_next
= IV_FREELIST_END
;
606 ivac
->ivac_freelist
= 1;
607 ivac_lock_init(ivac
);
608 ivac
->ivac_key_index
= key_index
;
614 ivac_dealloc(ipc_voucher_attr_control_t ivac
)
616 ipc_voucher_attr_manager_t ivam
= IVAM_NULL
;
617 iv_index_t key_index
= ivac
->ivac_key_index
;
618 ipc_port_t port
= ivac
->ivac_port
;
622 * If the control is in the global table, we
623 * have to remove it from there before we (re)confirm
624 * that the reference count is still zero.
627 if (os_ref_get_count(&ivac
->ivac_refs
) > 0) {
632 /* take it out of the global table */
633 if (iv_global_table
[key_index
].ivgte_control
== ivac
) {
634 ivam
= iv_global_table
[key_index
].ivgte_manager
;
635 iv_global_table
[key_index
].ivgte_manager
= IVAM_NULL
;
636 iv_global_table
[key_index
].ivgte_control
= IVAC_NULL
;
637 iv_global_table
[key_index
].ivgte_key
= MACH_VOUCHER_ATTR_KEY_NONE
;
641 /* release the reference held on the resource manager */
642 if (IVAM_NULL
!= ivam
) {
643 (ivam
->ivam_release
)(ivam
);
647 * if a port was allocated for this voucher,
648 * it must not have any remaining send rights,
649 * because the port's reference on the voucher
650 * is gone. We can just discard it now.
652 if (IP_VALID(port
)) {
653 assert(ip_active(port
));
654 assert(port
->ip_srights
== 0);
656 ipc_port_dealloc_kernel(port
);
660 * the resource manager's control reference and all references
661 * held by the specific value caches are gone, so free the
665 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
666 if (ivac
->ivac_table
[i
].ivace_refs
!= 0) {
667 panic("deallocing a resource manager with live refs to its attr values\n");
671 kfree(ivac
->ivac_table
, ivac
->ivac_table_size
* sizeof(*ivac
->ivac_table
));
672 ivac_lock_destroy(ivac
);
673 zfree(ipc_voucher_attr_control_zone
, ivac
);
677 ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control
)
679 ivac_reference(control
);
683 ipc_voucher_attr_control_release(ipc_voucher_attr_control_t control
)
685 ivac_release(control
);
689 * Routine: convert_port_to_voucher_attr_control reference
691 * Convert from a port to a voucher attribute control.
692 * Doesn't consume the port ref; produces a voucher ref,
697 ipc_voucher_attr_control_t
698 convert_port_to_voucher_attr_control(
701 if (IP_VALID(port
)) {
702 ipc_voucher_attr_control_t ivac
= (ipc_voucher_attr_control_t
) port
->ip_kobject
;
705 * No need to lock because we have a reference on the
706 * port, and if it is a true voucher control port,
707 * that reference keeps the voucher bound to the port
710 if (ip_kotype(port
) != IKOT_VOUCHER_ATTR_CONTROL
) {
714 assert(ip_active(port
));
716 ivac_reference(ivac
);
723 ipc_voucher_attr_control_notify(mach_msg_header_t
*msg
)
725 mach_no_senders_notification_t
*notification
= (void *)msg
;
726 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
727 ipc_voucher_attr_control_t ivac
;
729 assert(IKOT_VOUCHER_ATTR_CONTROL
== ip_kotype(port
));
731 assert(ip_active(port
));
733 /* if no new send rights, drop a control reference */
734 if (port
->ip_mscount
== notification
->not_count
) {
735 ivac
= (ipc_voucher_attr_control_t
)port
->ip_kobject
;
745 * Convert a voucher attr control to a port.
748 convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control
)
750 ipc_port_t port
, send
;
752 if (IVAC_NULL
== control
) {
756 /* create a port if needed */
757 port
= control
->ivac_port
;
758 if (!IP_VALID(port
)) {
759 port
= ipc_port_alloc_kernel();
760 assert(IP_VALID(port
));
761 if (OSCompareAndSwapPtr(IP_NULL
, port
, &control
->ivac_port
)) {
763 ipc_kobject_set_atomically(port
, (ipc_kobject_t
) control
, IKOT_VOUCHER_ATTR_CONTROL
);
765 ipc_port_dealloc_kernel(port
);
766 port
= control
->ivac_port
;
768 assert(ip_kotype(port
) == IKOT_VOUCHER_ATTR_CONTROL
);
769 assert(port
->ip_kobject
== (ipc_kobject_t
)control
);
775 assert(ip_active(port
));
776 send
= ipc_port_make_send_locked(port
);
778 if (1 == port
->ip_srights
) {
779 ipc_port_t old_notify
;
781 /* transfer our ref to the port, and arm the no-senders notification */
782 assert(IP_NULL
== port
->ip_nsrequest
);
783 ipc_port_nsrequest(port
, port
->ip_mscount
, ipc_port_make_sonce_locked(port
), &old_notify
);
784 assert(IP_NULL
== old_notify
);
785 /* ipc_port_nsrequest unlocks the port */
787 /* piggyback on the existing port reference, so consume ours */
789 ivac_release(control
);
795 * Look up the values for a given <key, index> pair.
799 iv_index_t key_index
,
800 iv_index_t value_index
,
801 mach_voucher_attr_value_handle_array_t values
,
802 mach_voucher_attr_value_handle_array_size_t
*count
)
804 ipc_voucher_attr_control_t ivac
;
807 if (IV_UNUSED_VALINDEX
== value_index
||
808 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
<= key_index
) {
813 ivac
= iv_global_table
[key_index
].ivgte_control
;
814 assert(IVAC_NULL
!= ivac
);
817 * Get the entry and then the linked values.
820 assert(value_index
< ivac
->ivac_table_size
);
821 ivace
= &ivac
->ivac_table
[value_index
];
824 * TODO: support chained values (for effective vouchers).
826 assert(ivace
->ivace_refs
> 0);
827 values
[0] = ivace
->ivace_value
;
833 * ivac_grow_table - Allocate a bigger table of attribute values
835 * Conditions: ivac is locked on entry and again on return
838 ivac_grow_table(ipc_voucher_attr_control_t ivac
)
842 /* NOTE: do not modify *_table and *_size values once set */
843 ivac_entry_t new_table
= NULL
, old_table
= NULL
;
844 iv_index_t new_size
, old_size
;
846 if (ivac
->ivac_is_growing
) {
851 ivac
->ivac_is_growing
= 1;
852 if (ivac
->ivac_table_size
>= IVAC_ENTRIES_MAX
) {
853 panic("Cannot grow ipc space beyond IVAC_ENTRIES_MAX. Some process is leaking vouchers");
857 old_size
= ivac
->ivac_table_size
;
860 new_size
= old_size
* 2;
862 assert(new_size
> old_size
);
863 assert(new_size
< IVAC_ENTRIES_MAX
);
865 new_table
= kalloc(sizeof(ivac_entry
) * new_size
);
867 panic("Failed to grow ivac table to size %d\n", new_size
);
871 /* setup the free list for new entries */
872 for (i
= old_size
; i
< new_size
; i
++) {
873 ivace_reset_data(&new_table
[i
], i
+ 1);
878 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
879 ivace_copy_data(&ivac
->ivac_table
[i
], &new_table
[i
]);
882 old_table
= ivac
->ivac_table
;
884 ivac
->ivac_table
= new_table
;
885 ivac
->ivac_table_size
= new_size
;
887 /* adding new free entries at head of freelist */
888 ivac
->ivac_table
[new_size
- 1].ivace_next
= ivac
->ivac_freelist
;
889 ivac
->ivac_freelist
= old_size
;
890 ivac
->ivac_is_growing
= 0;
895 kfree(old_table
, old_size
* sizeof(ivac_entry
));
901 * ivace_reference_by_index
903 * Take an additional reference on the <key_index, val_index>
904 * cached value. It is assumed the caller already holds a
905 * reference to the same cached key-value pair.
908 ivace_reference_by_index(
909 iv_index_t key_index
,
910 iv_index_t val_index
)
912 ipc_voucher_attr_control_t ivac
;
915 if (IV_UNUSED_VALINDEX
== val_index
) {
919 ivgt_lookup(key_index
, FALSE
, NULL
, &ivac
);
920 assert(IVAC_NULL
!= ivac
);
923 assert(val_index
< ivac
->ivac_table_size
);
924 ivace
= &ivac
->ivac_table
[val_index
];
926 assert(0xdeadc0dedeadc0de != ivace
->ivace_value
);
927 assert(0 < ivace
->ivace_refs
);
928 assert(!ivace
->ivace_free
);
930 /* Take ref only on non-persistent values */
931 if (!ivace
->ivace_persist
) {
939 * Look up the values for a given <key, index> pair.
941 * Consumes a reference on the passed voucher control.
942 * Either it is donated to a newly-created value cache
943 * or it is released (if we piggy back on an existing
944 * value cache entry).
947 ivace_reference_by_value(
948 ipc_voucher_attr_control_t ivac
,
949 mach_voucher_attr_value_handle_t value
,
950 mach_voucher_attr_value_flags_t flag
)
952 ivac_entry_t ivace
= IVACE_NULL
;
953 iv_index_t hash_index
;
956 if (IVAC_NULL
== ivac
) {
957 return IV_UNUSED_VALINDEX
;
962 hash_index
= IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
963 index
= ivac
->ivac_table
[hash_index
].ivace_index
;
964 while (index
!= IV_HASH_END
) {
965 assert(index
< ivac
->ivac_table_size
);
966 ivace
= &ivac
->ivac_table
[index
];
967 assert(!ivace
->ivace_free
);
969 if (ivace
->ivace_value
== value
) {
973 assert(ivace
->ivace_next
!= index
);
974 index
= ivace
->ivace_next
;
978 if (index
!= IV_HASH_END
) {
979 /* only add reference on non-persistent value */
980 if (!ivace
->ivace_persist
) {
990 /* insert new entry in the table */
991 index
= ivac
->ivac_freelist
;
992 if (IV_FREELIST_END
== index
) {
994 ivac_grow_table(ivac
);
998 /* take the entry off the freelist */
999 ivace
= &ivac
->ivac_table
[index
];
1000 ivac
->ivac_freelist
= ivace
->ivace_next
;
1002 /* initialize the new entry */
1003 ivace
->ivace_value
= value
;
1004 ivace
->ivace_refs
= 1;
1005 ivace
->ivace_made
= 1;
1006 ivace
->ivace_free
= FALSE
;
1007 ivace
->ivace_persist
= (flag
& MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
) ? TRUE
: FALSE
;
1009 /* insert the new entry in the proper hash chain */
1010 ivace
->ivace_next
= ivac
->ivac_table
[hash_index
].ivace_index
;
1011 ivac
->ivac_table
[hash_index
].ivace_index
= index
;
1014 /* donated passed in ivac reference to new entry */
1020 * Release a reference on the given <key_index, value_index> pair.
1022 * Conditions: called with nothing locked, as it may cause
1023 * callouts and/or messaging to the resource
1028 iv_index_t key_index
,
1029 iv_index_t value_index
)
1031 ipc_voucher_attr_control_t ivac
;
1032 ipc_voucher_attr_manager_t ivam
;
1033 mach_voucher_attr_value_handle_t value
;
1034 mach_voucher_attr_value_reference_t made
;
1035 mach_voucher_attr_key_t key
;
1036 iv_index_t hash_index
;
1040 /* cant release the default value */
1041 if (IV_UNUSED_VALINDEX
== value_index
) {
1045 ivgt_lookup(key_index
, FALSE
, &ivam
, &ivac
);
1046 assert(IVAC_NULL
!= ivac
);
1047 assert(IVAM_NULL
!= ivam
);
1050 assert(value_index
< ivac
->ivac_table_size
);
1051 ivace
= &ivac
->ivac_table
[value_index
];
1053 assert(0 < ivace
->ivace_refs
);
1055 /* cant release persistent values */
1056 if (ivace
->ivace_persist
) {
1061 if (0 < --ivace
->ivace_refs
) {
1066 key
= iv_index_to_key(key_index
);
1067 assert(MACH_VOUCHER_ATTR_KEY_NONE
!= key
);
1070 * if last return reply is still pending,
1071 * let it handle this later return when
1072 * the previous reply comes in.
1074 if (ivace
->ivace_releasing
) {
1079 /* claim releasing */
1080 ivace
->ivace_releasing
= TRUE
;
1081 value
= ivace
->ivace_value
;
1084 assert(value
== ivace
->ivace_value
);
1085 assert(!ivace
->ivace_free
);
1086 made
= ivace
->ivace_made
;
1089 /* callout to manager's release_value */
1090 kr
= (ivam
->ivam_release_value
)(ivam
, key
, value
, made
);
1092 /* recalculate entry address as table may have changed */
1094 ivace
= &ivac
->ivac_table
[value_index
];
1095 assert(value
== ivace
->ivace_value
);
1098 * new made values raced with this return. If the
1099 * manager OK'ed the prior release, we have to start
1100 * the made numbering over again (pretend the race
1101 * didn't happen). If the entry has zero refs again,
1102 * re-drive the release.
1104 if (ivace
->ivace_made
!= made
) {
1105 if (KERN_SUCCESS
== kr
) {
1106 ivace
->ivace_made
-= made
;
1109 if (0 == ivace
->ivace_refs
) {
1113 ivace
->ivace_releasing
= FALSE
;
1118 * If the manager returned FAILURE, someone took a
1119 * reference on the value but have not updated the ivace,
1120 * release the lock and return since thread who got
1121 * the new reference will update the ivace and will have
1122 * non-zero reference on the value.
1124 if (KERN_SUCCESS
!= kr
) {
1125 ivace
->ivace_releasing
= FALSE
;
1131 assert(0 == ivace
->ivace_refs
);
1134 * going away - remove entry from its hash
1135 * If its at the head of the hash bucket list (common), unchain
1136 * at the head. Otherwise walk the chain until the next points
1137 * at this entry, and remove it from the the list there.
1139 hash_index
= iv_hash_value(key_index
, value
);
1140 if (ivac
->ivac_table
[hash_index
].ivace_index
== value_index
) {
1141 ivac
->ivac_table
[hash_index
].ivace_index
= ivace
->ivace_next
;
1143 hash_index
= ivac
->ivac_table
[hash_index
].ivace_index
;
1144 assert(IV_HASH_END
!= hash_index
);
1145 while (ivac
->ivac_table
[hash_index
].ivace_next
!= value_index
) {
1146 hash_index
= ivac
->ivac_table
[hash_index
].ivace_next
;
1147 assert(IV_HASH_END
!= hash_index
);
1149 ivac
->ivac_table
[hash_index
].ivace_next
= ivace
->ivace_next
;
1152 /* Put this entry on the freelist */
1153 ivace
->ivace_value
= 0xdeadc0dedeadc0de;
1154 ivace
->ivace_releasing
= FALSE
;
1155 ivace
->ivace_free
= TRUE
;
1156 ivace
->ivace_made
= 0;
1157 ivace
->ivace_next
= ivac
->ivac_freelist
;
1158 ivac
->ivac_freelist
= value_index
;
1161 /* release the reference this value held on its cache control */
1171 * Lookup an entry in the global table from the context of a manager
1172 * registration. Adds a reference to the control to keep the results
1173 * around (if needed).
1175 * Because of the calling point, we can't be sure the manager is
1176 * [fully] registered yet. So, we must hold the global table lock
1177 * during the lookup to synchronize with in-parallel registrations
1178 * (and possible table growth).
1181 ivgt_lookup(iv_index_t key_index
,
1182 boolean_t take_reference
,
1183 ipc_voucher_attr_manager_t
*manager
,
1184 ipc_voucher_attr_control_t
*control
)
1186 ipc_voucher_attr_control_t ivac
;
1188 if (key_index
< MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
) {
1190 if (NULL
!= manager
) {
1191 *manager
= iv_global_table
[key_index
].ivgte_manager
;
1193 ivac
= iv_global_table
[key_index
].ivgte_control
;
1194 if (IVAC_NULL
!= ivac
) {
1195 assert(key_index
== ivac
->ivac_key_index
);
1196 if (take_reference
) {
1197 assert(NULL
!= control
);
1198 ivac_reference(ivac
);
1202 if (NULL
!= control
) {
1206 if (NULL
!= manager
) {
1207 *manager
= IVAM_NULL
;
1209 if (NULL
!= control
) {
1210 *control
= IVAC_NULL
;
1216 * Routine: ipc_replace_voucher_value
1218 * Replace the <voucher, key> value with the results of
1219 * running the supplied command through the resource
1220 * manager's get-value callback.
1222 * Nothing locked (may invoke user-space repeatedly).
1223 * Caller holds references on voucher and previous voucher.
1225 static kern_return_t
1226 ipc_replace_voucher_value(
1227 ipc_voucher_t voucher
,
1228 mach_voucher_attr_key_t key
,
1229 mach_voucher_attr_recipe_command_t command
,
1230 ipc_voucher_t prev_voucher
,
1231 mach_voucher_attr_content_t content
,
1232 mach_voucher_attr_content_size_t content_size
)
1234 mach_voucher_attr_value_handle_t previous_vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1235 mach_voucher_attr_value_handle_array_size_t previous_vals_count
;
1236 mach_voucher_attr_value_handle_t new_value
;
1237 mach_voucher_attr_value_flags_t new_flag
;
1238 ipc_voucher_t new_value_voucher
;
1239 ipc_voucher_attr_manager_t ivam
;
1240 ipc_voucher_attr_control_t ivac
;
1241 iv_index_t prev_val_index
;
1242 iv_index_t save_val_index
;
1243 iv_index_t val_index
;
1244 iv_index_t key_index
;
1248 * Get the manager for this key_index.
1249 * Returns a reference on the control.
1251 key_index
= iv_key_to_index(key
);
1252 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1253 if (IVAM_NULL
== ivam
) {
1254 return KERN_INVALID_ARGUMENT
;
1257 /* save the current value stored in the forming voucher */
1258 save_val_index
= iv_lookup(voucher
, key_index
);
1261 * Get the previous value(s) for this key creation.
1262 * If a previous voucher is specified, they come from there.
1263 * Otherwise, they come from the intermediate values already
1264 * in the forming voucher.
1266 prev_val_index
= (IV_NULL
!= prev_voucher
) ?
1267 iv_lookup(prev_voucher
, key_index
) :
1269 ivace_lookup_values(key_index
, prev_val_index
,
1270 previous_vals
, &previous_vals_count
);
1272 /* Call out to resource manager to get new value */
1273 new_value_voucher
= IV_NULL
;
1274 kr
= (ivam
->ivam_get_value
)(
1276 previous_vals
, previous_vals_count
,
1277 content
, content_size
,
1278 &new_value
, &new_flag
, &new_value_voucher
);
1279 if (KERN_SUCCESS
!= kr
) {
1284 /* TODO: value insertion from returned voucher */
1285 if (IV_NULL
!= new_value_voucher
) {
1286 iv_release(new_value_voucher
);
1290 * Find or create a slot in the table associated
1291 * with this attribute value. The ivac reference
1292 * is transferred to a new value, or consumed if
1293 * we find a matching existing value.
1295 val_index
= ivace_reference_by_value(ivac
, new_value
, new_flag
);
1296 iv_set(voucher
, key_index
, val_index
);
1299 * release saved old value from the newly forming voucher
1300 * This is saved until the end to avoid churning the
1301 * release logic in cases where the same value is returned
1302 * as was there before.
1304 ivace_release(key_index
, save_val_index
);
1306 return KERN_SUCCESS
;
1310 * Routine: ipc_directly_replace_voucher_value
1312 * Replace the <voucher, key> value with the value-handle
1313 * supplied directly by the attribute manager.
1316 * Caller holds references on voucher.
1317 * A made reference to the value-handle is donated by the caller.
1319 static kern_return_t
1320 ipc_directly_replace_voucher_value(
1321 ipc_voucher_t voucher
,
1322 mach_voucher_attr_key_t key
,
1323 mach_voucher_attr_value_handle_t new_value
)
1325 ipc_voucher_attr_manager_t ivam
;
1326 ipc_voucher_attr_control_t ivac
;
1327 iv_index_t save_val_index
;
1328 iv_index_t val_index
;
1329 iv_index_t key_index
;
1332 * Get the manager for this key_index.
1333 * Returns a reference on the control.
1335 key_index
= iv_key_to_index(key
);
1336 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1337 if (IVAM_NULL
== ivam
) {
1338 return KERN_INVALID_ARGUMENT
;
1341 /* save the current value stored in the forming voucher */
1342 save_val_index
= iv_lookup(voucher
, key_index
);
1345 * Find or create a slot in the table associated
1346 * with this attribute value. The ivac reference
1347 * is transferred to a new value, or consumed if
1348 * we find a matching existing value.
1350 val_index
= ivace_reference_by_value(ivac
, new_value
,
1351 MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
);
1352 iv_set(voucher
, key_index
, val_index
);
1355 * release saved old value from the newly forming voucher
1356 * This is saved until the end to avoid churning the
1357 * release logic in cases where the same value is returned
1358 * as was there before.
1360 ivace_release(key_index
, save_val_index
);
1362 return KERN_SUCCESS
;
1365 static kern_return_t
1366 ipc_execute_voucher_recipe_command(
1367 ipc_voucher_t voucher
,
1368 mach_voucher_attr_key_t key
,
1369 mach_voucher_attr_recipe_command_t command
,
1370 ipc_voucher_t prev_iv
,
1371 mach_voucher_attr_content_t content
,
1372 mach_voucher_attr_content_size_t content_size
,
1375 iv_index_t prev_val_index
;
1376 iv_index_t val_index
;
1381 * MACH_VOUCHER_ATTR_COPY
1382 * Copy the attribute(s) from the previous voucher to the new
1383 * one. A wildcard key is an acceptable value - indicating a
1384 * desire to copy all the attribute values from the previous
1387 case MACH_VOUCHER_ATTR_COPY
:
1389 /* no recipe data on a copy */
1390 if (0 < content_size
) {
1391 return KERN_INVALID_ARGUMENT
;
1394 /* nothing to copy from? - done */
1395 if (IV_NULL
== prev_iv
) {
1396 return KERN_SUCCESS
;
1399 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1400 iv_index_t limit
, j
;
1402 /* reconcile possible difference in voucher sizes */
1403 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1404 prev_iv
->iv_table_size
:
1405 voucher
->iv_table_size
;
1407 /* wildcard matching */
1408 for (j
= 0; j
< limit
; j
++) {
1409 /* release old value being replaced */
1410 val_index
= iv_lookup(voucher
, j
);
1411 ivace_release(j
, val_index
);
1413 /* replace with reference to prev voucher's value */
1414 prev_val_index
= iv_lookup(prev_iv
, j
);
1415 ivace_reference_by_index(j
, prev_val_index
);
1416 iv_set(voucher
, j
, prev_val_index
);
1419 iv_index_t key_index
;
1421 /* copy just one key */
1422 key_index
= iv_key_to_index(key
);
1423 if (ivgt_keys_in_use
< key_index
) {
1424 return KERN_INVALID_ARGUMENT
;
1427 /* release old value being replaced */
1428 val_index
= iv_lookup(voucher
, key_index
);
1429 ivace_release(key_index
, val_index
);
1431 /* replace with reference to prev voucher's value */
1432 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1433 ivace_reference_by_index(key_index
, prev_val_index
);
1434 iv_set(voucher
, key_index
, prev_val_index
);
1439 * MACH_VOUCHER_ATTR_REMOVE
1440 * Remove the attribute(s) from the under construction voucher.
1441 * A wildcard key is an acceptable value - indicating a desire
1442 * to remove all the attribute values set up so far in the voucher.
1443 * If a previous voucher is specified, only remove the value it
1444 * it matches the value in the previous voucher.
1446 case MACH_VOUCHER_ATTR_REMOVE
:
1447 /* no recipe data on a remove */
1448 if (0 < content_size
) {
1449 return KERN_INVALID_ARGUMENT
;
1452 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1453 iv_index_t limit
, j
;
1455 /* reconcile possible difference in voucher sizes */
1456 limit
= (IV_NULL
== prev_iv
) ? voucher
->iv_table_size
:
1457 ((prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1458 prev_iv
->iv_table_size
: voucher
->iv_table_size
);
1460 /* wildcard matching */
1461 for (j
= 0; j
< limit
; j
++) {
1462 val_index
= iv_lookup(voucher
, j
);
1464 /* If not matched in previous, skip */
1465 if (IV_NULL
!= prev_iv
) {
1466 prev_val_index
= iv_lookup(prev_iv
, j
);
1467 if (val_index
!= prev_val_index
) {
1471 /* release and clear */
1472 ivace_release(j
, val_index
);
1473 iv_set(voucher
, j
, IV_UNUSED_VALINDEX
);
1476 iv_index_t key_index
;
1478 /* copy just one key */
1479 key_index
= iv_key_to_index(key
);
1480 if (ivgt_keys_in_use
< key_index
) {
1481 return KERN_INVALID_ARGUMENT
;
1484 val_index
= iv_lookup(voucher
, key_index
);
1486 /* If not matched in previous, skip */
1487 if (IV_NULL
!= prev_iv
) {
1488 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1489 if (val_index
!= prev_val_index
) {
1494 /* release and clear */
1495 ivace_release(key_index
, val_index
);
1496 iv_set(voucher
, key_index
, IV_UNUSED_VALINDEX
);
1501 * MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
1502 * Use key-privilege to set a value handle for the attribute directly,
1503 * rather than triggering a callback into the attribute manager to
1504 * interpret a recipe to generate the value handle.
1506 case MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
:
1508 mach_voucher_attr_value_handle_t new_value
;
1510 if (sizeof(mach_voucher_attr_value_handle_t
) != content_size
) {
1511 return KERN_INVALID_ARGUMENT
;
1514 new_value
= *(mach_voucher_attr_value_handle_t
*)(void *)content
;
1515 kr
= ipc_directly_replace_voucher_value(voucher
,
1518 if (KERN_SUCCESS
!= kr
) {
1522 return KERN_INVALID_CAPABILITY
;
1527 * MACH_VOUCHER_ATTR_REDEEM
1528 * Redeem the attribute(s) from the previous voucher for a possibly
1529 * new value in the new voucher. A wildcard key is an acceptable value,
1530 * indicating a desire to redeem all the values.
1532 case MACH_VOUCHER_ATTR_REDEEM
:
1534 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1535 iv_index_t limit
, j
;
1537 /* reconcile possible difference in voucher sizes */
1538 if (IV_NULL
!= prev_iv
) {
1539 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1540 prev_iv
->iv_table_size
:
1541 voucher
->iv_table_size
;
1543 limit
= voucher
->iv_table_size
;
1546 /* wildcard matching */
1547 for (j
= 0; j
< limit
; j
++) {
1548 mach_voucher_attr_key_t j_key
;
1550 j_key
= iv_index_to_key(j
);
1552 /* skip non-existent managers */
1553 if (MACH_VOUCHER_ATTR_KEY_NONE
== j_key
) {
1557 /* get the new value from redeem (skip empty previous) */
1558 kr
= ipc_replace_voucher_value(voucher
,
1564 if (KERN_SUCCESS
!= kr
) {
1570 /* fall thru for single key redemption */
1574 * Replace the current value for the <voucher, key> pair with whatever
1575 * value the resource manager returns for the command and recipe
1576 * combination provided.
1579 kr
= ipc_replace_voucher_value(voucher
,
1585 if (KERN_SUCCESS
!= kr
) {
1591 return KERN_SUCCESS
;
1595 * Routine: iv_checksum
1597 * Compute the voucher sum. This is more position-
1598 * relevant than many other checksums - important for
1599 * vouchers (arrays of low, oft-reused, indexes).
1601 static inline iv_index_t
1602 iv_checksum(ipc_voucher_t voucher
, boolean_t
*emptyp
)
1606 boolean_t empty
= TRUE
;
1607 if (0 < voucher
->iv_table_size
) {
1608 iv_index_t i
= voucher
->iv_table_size
- 1;
1611 iv_index_t v
= voucher
->iv_table
[i
];
1612 c
= c
<< 3 | c
>> (32 - 3); /* rotate */
1613 c
= ~c
; /* invert */
1615 c
+= v
; /* add in */
1627 * See if the set of values represented by this new voucher
1628 * already exist in another voucher. If so return a reference
1629 * to the existing voucher and deallocate the voucher provided.
1630 * Otherwise, insert this one in the hash and return it.
1632 * A voucher reference is donated on entry.
1634 * A voucher reference (may be different than on entry).
1636 static ipc_voucher_t
1637 iv_dedup(ipc_voucher_t new_iv
)
1644 sum
= iv_checksum(new_iv
, &empty
);
1646 /* If all values are default, that's the empty (NULL) voucher */
1648 iv_dealloc(new_iv
, FALSE
);
1652 hash
= IV_HASH_BUCKET(sum
);
1655 queue_iterate(&ivht_bucket
[hash
], iv
, ipc_voucher_t
, iv_hash_link
) {
1656 assert(iv
->iv_hash
== hash
);
1658 /* if not already deallocating and sums match... */
1659 if ((os_ref_get_count(&iv
->iv_refs
) > 0) && (iv
->iv_sum
== sum
)) {
1662 assert(iv
->iv_table_size
<= new_iv
->iv_table_size
);
1664 /* and common entries match... */
1665 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
1666 if (iv
->iv_table
[i
] != new_iv
->iv_table
[i
]) {
1670 if (i
< iv
->iv_table_size
) {
1674 /* and all extra entries in new one are unused... */
1675 while (i
< new_iv
->iv_table_size
) {
1676 if (new_iv
->iv_table
[i
++] != IV_UNUSED_VALINDEX
) {
1680 if (i
< new_iv
->iv_table_size
) {
1684 /* ... we found a match... */
1686 /* can we get a ref before it hits 0
1688 * This is thread safe. If the reference count is zero before we
1689 * adjust it, no other thread can have a reference to the voucher.
1690 * The dealloc code requires holding the ivht_lock, so
1691 * the voucher cannot be yanked out from under us.
1693 if (!os_ref_retain_try(&iv
->iv_refs
)) {
1699 /* referenced previous, so deallocate the new one */
1700 iv_dealloc(new_iv
, FALSE
);
1705 /* add the new voucher to the hash, and return it */
1706 new_iv
->iv_sum
= sum
;
1707 new_iv
->iv_hash
= hash
;
1708 queue_enter(&ivht_bucket
[hash
], new_iv
, ipc_voucher_t
, iv_hash_link
);
1713 * This code is disabled for KDEBUG_LEVEL_IST and KDEBUG_LEVEL_NONE
1715 #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD)
1716 if (kdebug_enable
& ~KDEBUG_ENABLE_PPT
) {
1717 uintptr_t voucher_addr
= VM_KERNEL_ADDRPERM((uintptr_t)new_iv
);
1718 uintptr_t attr_tracepoints_needed
= 0;
1720 if (ipc_voucher_trace_contents
) {
1722 * voucher_contents sizing is a bit more constrained
1723 * than might be obvious.
1725 * This is typically a uint8_t typed array. However,
1726 * we want to access it as a uintptr_t to efficiently
1727 * copyout the data in tracepoints.
1729 * This constrains the size to uintptr_t bytes, and
1730 * adds a minimimum alignment requirement equivalent
1733 * Further constraining the size is the fact that it
1734 * is copied out 4 uintptr_t chunks at a time. We do
1735 * NOT want to run off the end of the array and copyout
1736 * random stack data.
1738 * So the minimum size is 4 * sizeof(uintptr_t), and
1739 * the minimum alignment is uintptr_t aligned.
1742 #define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
1743 #define PAYLOAD_SIZE 1024
1745 static_assert(PAYLOAD_SIZE
% PAYLOAD_PER_TRACEPOINT
== 0, "size invariant violated");
1747 mach_voucher_attr_raw_recipe_array_size_t payload_size
= PAYLOAD_SIZE
;
1748 uintptr_t payload
[PAYLOAD_SIZE
/ sizeof(uintptr_t)];
1751 kr
= mach_voucher_extract_all_attr_recipes(new_iv
, (mach_voucher_attr_raw_recipe_array_t
)payload
, &payload_size
);
1752 if (KERN_SUCCESS
== kr
) {
1753 attr_tracepoints_needed
= (payload_size
+ PAYLOAD_PER_TRACEPOINT
- 1) / PAYLOAD_PER_TRACEPOINT
;
1756 * To prevent leaking data from the stack, we
1757 * need to zero data to the end of a tracepoint
1760 size_t remainder
= payload_size
% PAYLOAD_PER_TRACEPOINT
;
1762 bzero((uint8_t*)payload
+ payload_size
,
1763 PAYLOAD_PER_TRACEPOINT
- remainder
);
1767 KDBG(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_CREATE
),
1768 voucher_addr
, new_iv
->iv_table_size
, ivht_count
,
1771 uintptr_t index
= 0;
1772 while (attr_tracepoints_needed
--) {
1773 KDBG(MACHDBG_CODE(DBG_MACH_IPC
,
1774 MACH_IPC_VOUCHER_CREATE_ATTR_DATA
), payload
[index
],
1775 payload
[index
+ 1], payload
[index
+ 2],
1776 payload
[index
+ 3]);
1780 KDBG(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_CREATE
),
1781 voucher_addr
, new_iv
->iv_table_size
, ivht_count
);
1784 #endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
1790 * Routine: ipc_create_mach_voucher
1792 * Create a new mach voucher and initialize it with the
1793 * value(s) created by having the appropriate resource
1794 * managers interpret the supplied recipe commands and
1797 * Nothing locked (may invoke user-space repeatedly).
1798 * Caller holds references on previous vouchers.
1799 * Previous vouchers are passed as voucher indexes.
1802 ipc_create_mach_voucher(
1803 ipc_voucher_attr_raw_recipe_array_t recipes
,
1804 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1805 ipc_voucher_t
*new_voucher
)
1807 ipc_voucher_attr_recipe_t sub_recipe
;
1808 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1809 ipc_voucher_t voucher
;
1810 kern_return_t kr
= KERN_SUCCESS
;
1812 /* if nothing to do ... */
1813 if (0 == recipe_size
) {
1814 *new_voucher
= IV_NULL
;
1815 return KERN_SUCCESS
;
1818 /* allocate a voucher */
1819 voucher
= iv_alloc(ivgt_keys_in_use
);
1820 if (IV_NULL
== voucher
) {
1821 return KERN_RESOURCE_SHORTAGE
;
1824 /* iterate over the recipe items */
1825 while (0 < recipe_size
- recipe_used
) {
1826 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1827 kr
= KERN_INVALID_ARGUMENT
;
1831 /* find the next recipe */
1832 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1833 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1834 kr
= KERN_INVALID_ARGUMENT
;
1837 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1839 kr
= ipc_execute_voucher_recipe_command(voucher
,
1841 sub_recipe
->command
,
1842 sub_recipe
->previous_voucher
,
1843 sub_recipe
->content
,
1844 sub_recipe
->content_size
,
1846 if (KERN_SUCCESS
!= kr
) {
1851 if (KERN_SUCCESS
== kr
) {
1852 *new_voucher
= iv_dedup(voucher
);
1854 iv_dealloc(voucher
, FALSE
);
1855 *new_voucher
= IV_NULL
;
1861 * Routine: ipc_voucher_attr_control_create_mach_voucher
1863 * Create a new mach voucher and initialize it with the
1864 * value(s) created by having the appropriate resource
1865 * managers interpret the supplied recipe commands and
1868 * The resource manager control's privilege over its
1869 * particular key value is reflected on to the execution
1870 * code, allowing internal commands (like setting a
1871 * key value handle directly, rather than having to
1872 * create a recipe, that will generate a callback just
1876 * Nothing locked (may invoke user-space repeatedly).
1877 * Caller holds references on previous vouchers.
1878 * Previous vouchers are passed as voucher indexes.
1881 ipc_voucher_attr_control_create_mach_voucher(
1882 ipc_voucher_attr_control_t control
,
1883 ipc_voucher_attr_raw_recipe_array_t recipes
,
1884 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1885 ipc_voucher_t
*new_voucher
)
1887 mach_voucher_attr_key_t control_key
;
1888 ipc_voucher_attr_recipe_t sub_recipe
;
1889 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1890 ipc_voucher_t voucher
= IV_NULL
;
1891 kern_return_t kr
= KERN_SUCCESS
;
1893 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
1894 return KERN_INVALID_CAPABILITY
;
1897 /* if nothing to do ... */
1898 if (0 == recipe_size
) {
1899 *new_voucher
= IV_NULL
;
1900 return KERN_SUCCESS
;
1903 /* allocate new voucher */
1904 voucher
= iv_alloc(ivgt_keys_in_use
);
1905 if (IV_NULL
== voucher
) {
1906 return KERN_RESOURCE_SHORTAGE
;
1909 control_key
= iv_index_to_key(control
->ivac_key_index
);
1911 /* iterate over the recipe items */
1912 while (0 < recipe_size
- recipe_used
) {
1913 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1914 kr
= KERN_INVALID_ARGUMENT
;
1918 /* find the next recipe */
1919 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1920 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1921 kr
= KERN_INVALID_ARGUMENT
;
1924 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1926 kr
= ipc_execute_voucher_recipe_command(voucher
,
1928 sub_recipe
->command
,
1929 sub_recipe
->previous_voucher
,
1930 sub_recipe
->content
,
1931 sub_recipe
->content_size
,
1932 (sub_recipe
->key
== control_key
));
1933 if (KERN_SUCCESS
!= kr
) {
1938 if (KERN_SUCCESS
== kr
) {
1939 *new_voucher
= iv_dedup(voucher
);
1941 *new_voucher
= IV_NULL
;
1942 iv_dealloc(voucher
, FALSE
);
1948 * ipc_register_well_known_mach_voucher_attr_manager
1950 * Register the resource manager responsible for a given key value.
1953 ipc_register_well_known_mach_voucher_attr_manager(
1954 ipc_voucher_attr_manager_t manager
,
1955 mach_voucher_attr_value_handle_t default_value
,
1956 mach_voucher_attr_key_t key
,
1957 ipc_voucher_attr_control_t
*control
)
1959 ipc_voucher_attr_control_t new_control
;
1960 iv_index_t key_index
;
1961 iv_index_t hash_index
;
1963 if (IVAM_NULL
== manager
) {
1964 return KERN_INVALID_ARGUMENT
;
1967 key_index
= iv_key_to_index(key
);
1968 if (IV_UNUSED_KEYINDEX
== key_index
) {
1969 return KERN_INVALID_ARGUMENT
;
1972 new_control
= ivac_alloc(key_index
);
1973 if (IVAC_NULL
== new_control
) {
1974 return KERN_RESOURCE_SHORTAGE
;
1977 /* insert the default value into slot 0 */
1978 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_value
= default_value
;
1979 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_refs
= IVACE_REFS_MAX
;
1980 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_made
= IVACE_REFS_MAX
;
1981 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_persist
= TRUE
;
1982 assert(IV_HASH_END
== new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_next
);
1985 if (IVAM_NULL
!= iv_global_table
[key_index
].ivgte_manager
) {
1987 ivac_release(new_control
);
1988 return KERN_INVALID_ARGUMENT
;
1991 /* fill in the global table slot for this key */
1992 iv_global_table
[key_index
].ivgte_manager
= manager
;
1993 iv_global_table
[key_index
].ivgte_control
= new_control
;
1994 iv_global_table
[key_index
].ivgte_key
= key
;
1996 /* insert the default value into the hash (in case it is returned later) */
1997 hash_index
= iv_hash_value(key_index
, default_value
);
1998 assert(IV_HASH_END
== new_control
->ivac_table
[hash_index
].ivace_index
);
1999 new_control
->ivac_table
[hash_index
].ivace_index
= IV_UNUSED_VALINDEX
;
2003 /* return the reference on the new cache control to the caller */
2004 *control
= new_control
;
2006 return KERN_SUCCESS
;
2010 * Routine: mach_voucher_extract_attr_content
2012 * Extract the content for a given <voucher, key> pair.
2014 * If a value other than the default is present for this
2015 * <voucher,key> pair, we need to contact the resource
2016 * manager to extract the content/meaning of the value(s)
2017 * present. Otherwise, return success (but no data).
2020 * Nothing locked - as it may upcall to user-space.
2021 * The caller holds a reference on the voucher.
2024 mach_voucher_extract_attr_content(
2025 ipc_voucher_t voucher
,
2026 mach_voucher_attr_key_t key
,
2027 mach_voucher_attr_content_t content
,
2028 mach_voucher_attr_content_size_t
*in_out_size
)
2030 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2031 mach_voucher_attr_value_handle_array_size_t vals_count
;
2032 mach_voucher_attr_recipe_command_t command
;
2033 ipc_voucher_attr_manager_t manager
;
2034 iv_index_t value_index
;
2035 iv_index_t key_index
;
2039 if (IV_NULL
== voucher
) {
2040 return KERN_INVALID_ARGUMENT
;
2043 key_index
= iv_key_to_index(key
);
2045 value_index
= iv_lookup(voucher
, key_index
);
2046 if (IV_UNUSED_VALINDEX
== value_index
) {
2048 return KERN_SUCCESS
;
2052 * Get the manager for this key_index. The
2053 * existence of a non-default value for this
2054 * slot within our voucher will keep the
2055 * manager referenced during the callout.
2057 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2058 if (IVAM_NULL
== manager
) {
2059 return KERN_INVALID_ARGUMENT
;
2063 * Get the value(s) to pass to the manager
2064 * for this value_index.
2066 ivace_lookup_values(key_index
, value_index
,
2068 assert(0 < vals_count
);
2070 /* callout to manager */
2072 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2075 content
, in_out_size
);
2080 * Routine: mach_voucher_extract_attr_recipe
2082 * Extract a recipe for a given <voucher, key> pair.
2084 * If a value other than the default is present for this
2085 * <voucher,key> pair, we need to contact the resource
2086 * manager to extract the content/meaning of the value(s)
2087 * present. Otherwise, return success (but no data).
2090 * Nothing locked - as it may upcall to user-space.
2091 * The caller holds a reference on the voucher.
2094 mach_voucher_extract_attr_recipe(
2095 ipc_voucher_t voucher
,
2096 mach_voucher_attr_key_t key
,
2097 mach_voucher_attr_raw_recipe_t raw_recipe
,
2098 mach_voucher_attr_raw_recipe_size_t
*in_out_size
)
2100 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2101 mach_voucher_attr_value_handle_array_size_t vals_count
;
2102 ipc_voucher_attr_manager_t manager
;
2103 mach_voucher_attr_recipe_t recipe
;
2104 iv_index_t value_index
;
2105 iv_index_t key_index
;
2109 if (IV_NULL
== voucher
) {
2110 return KERN_INVALID_ARGUMENT
;
2113 key_index
= iv_key_to_index(key
);
2115 value_index
= iv_lookup(voucher
, key_index
);
2116 if (IV_UNUSED_VALINDEX
== value_index
) {
2118 return KERN_SUCCESS
;
2121 if (*in_out_size
< sizeof(*recipe
)) {
2122 return KERN_NO_SPACE
;
2125 recipe
= (mach_voucher_attr_recipe_t
)(void *)raw_recipe
;
2127 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2128 recipe
->previous_voucher
= MACH_VOUCHER_NAME_NULL
;
2129 recipe
->content_size
= *in_out_size
- sizeof(*recipe
);
2132 * Get the manager for this key_index. The
2133 * existence of a non-default value for this
2134 * slot within our voucher will keep the
2135 * manager referenced during the callout.
2137 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2138 if (IVAM_NULL
== manager
) {
2139 return KERN_INVALID_ARGUMENT
;
2143 * Get the value(s) to pass to the manager
2144 * for this value_index.
2146 ivace_lookup_values(key_index
, value_index
,
2148 assert(0 < vals_count
);
2150 /* callout to manager */
2151 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2154 recipe
->content
, &recipe
->content_size
);
2155 if (KERN_SUCCESS
== kr
) {
2156 assert(*in_out_size
- sizeof(*recipe
) >= recipe
->content_size
);
2157 *in_out_size
= sizeof(*recipe
) + recipe
->content_size
;
2166 * Routine: mach_voucher_extract_all_attr_recipes
2168 * Extract all the (non-default) contents for a given voucher,
2169 * building up a recipe that could be provided to a future
2170 * voucher creation call.
2172 * Nothing locked (may invoke user-space).
2173 * Caller holds a reference on the supplied voucher.
2176 mach_voucher_extract_all_attr_recipes(
2177 ipc_voucher_t voucher
,
2178 mach_voucher_attr_raw_recipe_array_t recipes
,
2179 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2181 mach_voucher_attr_recipe_size_t recipe_size
= *in_out_size
;
2182 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2183 iv_index_t key_index
;
2185 if (IV_NULL
== voucher
) {
2186 return KERN_INVALID_ARGUMENT
;
2189 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2190 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2191 mach_voucher_attr_value_handle_array_size_t vals_count
;
2192 mach_voucher_attr_content_size_t content_size
;
2193 ipc_voucher_attr_manager_t manager
;
2194 mach_voucher_attr_recipe_t recipe
;
2195 mach_voucher_attr_key_t key
;
2196 iv_index_t value_index
;
2199 /* don't output anything for a default value */
2200 value_index
= iv_lookup(voucher
, key_index
);
2201 if (IV_UNUSED_VALINDEX
== value_index
) {
2205 if (recipe_size
- recipe_used
< sizeof(*recipe
)) {
2206 return KERN_NO_SPACE
;
2210 * Get the manager for this key_index. The
2211 * existence of a non-default value for this
2212 * slot within our voucher will keep the
2213 * manager referenced during the callout.
2215 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2216 assert(IVAM_NULL
!= manager
);
2217 if (IVAM_NULL
== manager
) {
2221 recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2222 content_size
= recipe_size
- recipe_used
- sizeof(*recipe
);
2225 * Get the value(s) to pass to the manager
2226 * for this value_index.
2228 ivace_lookup_values(key_index
, value_index
,
2230 assert(0 < vals_count
);
2232 key
= iv_index_to_key(key_index
);
2235 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2236 recipe
->content_size
= content_size
;
2238 /* callout to manager */
2239 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2242 recipe
->content
, &recipe
->content_size
);
2243 if (KERN_SUCCESS
!= kr
) {
2247 assert(recipe
->content_size
<= content_size
);
2248 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2251 *in_out_size
= recipe_used
;
2252 return KERN_SUCCESS
;
2256 * Routine: mach_voucher_debug_info
2258 * Extract all the (non-default) contents for a given mach port name,
2259 * building up a recipe that could be provided to a future
2260 * voucher creation call.
2262 * Nothing locked (may invoke user-space).
2263 * Caller may not hold a reference on the supplied voucher.
2265 #if !(DEVELOPMENT || DEBUG)
2267 mach_voucher_debug_info(
2268 ipc_space_t __unused space
,
2269 mach_port_name_t __unused voucher_name
,
2270 mach_voucher_attr_raw_recipe_array_t __unused recipes
,
2271 mach_voucher_attr_raw_recipe_array_size_t __unused
*in_out_size
)
2273 return KERN_NOT_SUPPORTED
;
2277 mach_voucher_debug_info(
2279 mach_port_name_t voucher_name
,
2280 mach_voucher_attr_raw_recipe_array_t recipes
,
2281 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2283 ipc_voucher_t voucher
= IPC_VOUCHER_NULL
;
2285 ipc_port_t port
= MACH_PORT_NULL
;
2287 if (!MACH_PORT_VALID(voucher_name
)) {
2288 return KERN_INVALID_ARGUMENT
;
2291 kr
= ipc_port_translate_send(space
, voucher_name
, &port
);
2292 if (KERN_SUCCESS
!= kr
) {
2293 return KERN_INVALID_ARGUMENT
;
2296 voucher
= convert_port_to_voucher(port
);
2300 kr
= mach_voucher_extract_all_attr_recipes(voucher
, recipes
, in_out_size
);
2301 ipc_voucher_release(voucher
);
2305 return KERN_FAILURE
;
2310 * Routine: mach_voucher_attr_command
2312 * Invoke an attribute-specific command through this voucher.
2314 * The voucher layout, membership, etc... is not altered
2315 * through the execution of this command.
2318 * Nothing locked - as it may upcall to user-space.
2319 * The caller holds a reference on the voucher.
2322 mach_voucher_attr_command(
2323 ipc_voucher_t voucher
,
2324 mach_voucher_attr_key_t key
,
2325 mach_voucher_attr_command_t command
,
2326 mach_voucher_attr_content_t in_content
,
2327 mach_voucher_attr_content_size_t in_content_size
,
2328 mach_voucher_attr_content_t out_content
,
2329 mach_voucher_attr_content_size_t
*out_content_size
)
2331 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2332 mach_voucher_attr_value_handle_array_size_t vals_count
;
2333 ipc_voucher_attr_manager_t manager
;
2334 ipc_voucher_attr_control_t control
;
2335 iv_index_t value_index
;
2336 iv_index_t key_index
;
2340 if (IV_NULL
== voucher
) {
2341 return KERN_INVALID_ARGUMENT
;
2344 key_index
= iv_key_to_index(key
);
2347 * Get the manager for this key_index.
2348 * Allowing commands against the default value
2349 * for an attribute means that we have to hold
2350 * reference on the attribute manager control
2351 * to keep the manager around during the command
2354 ivgt_lookup(key_index
, TRUE
, &manager
, &control
);
2355 if (IVAM_NULL
== manager
) {
2356 return KERN_INVALID_ARGUMENT
;
2360 * Get the values for this <voucher, key> pair
2361 * to pass to the attribute manager. It is still
2362 * permissible to execute a command against the
2363 * default value (empty value array).
2365 value_index
= iv_lookup(voucher
, key_index
);
2366 ivace_lookup_values(key_index
, value_index
,
2369 /* callout to manager */
2370 kr
= (manager
->ivam_command
)(manager
, key
,
2373 in_content
, in_content_size
,
2374 out_content
, out_content_size
);
2376 /* release reference on control */
2377 ivac_release(control
);
2383 * Routine: mach_voucher_attr_control_get_values
2385 * For a given voucher, get the value handle associated with the
2386 * specified attribute manager.
2389 mach_voucher_attr_control_get_values(
2390 ipc_voucher_attr_control_t control
,
2391 ipc_voucher_t voucher
,
2392 mach_voucher_attr_value_handle_array_t out_values
,
2393 mach_voucher_attr_value_handle_array_size_t
*in_out_size
)
2395 iv_index_t key_index
, value_index
;
2397 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
2398 return KERN_INVALID_CAPABILITY
;
2401 if (IV_NULL
== voucher
) {
2402 return KERN_INVALID_ARGUMENT
;
2405 if (0 == *in_out_size
) {
2406 return KERN_SUCCESS
;
2409 key_index
= control
->ivac_key_index
;
2411 assert(os_ref_get_count(&voucher
->iv_refs
) > 0);
2412 value_index
= iv_lookup(voucher
, key_index
);
2413 ivace_lookup_values(key_index
, value_index
,
2414 out_values
, in_out_size
);
2415 return KERN_SUCCESS
;
2419 * Routine: mach_voucher_attr_control_create_mach_voucher
2421 * Create a new mach voucher and initialize it by processing the
2422 * supplied recipe(s).
2424 * Coming in on the attribute control port denotes special privileges
2425 * over they key associated with the control port.
2427 * Coming in from user-space, each recipe item will have a previous
2428 * recipe port name that needs to be converted to a voucher. Because
2429 * we can't rely on the port namespace to hold a reference on each
2430 * previous voucher port for the duration of processing that command,
2431 * we have to convert the name to a voucher reference and release it
2432 * after the command processing is done.
2435 mach_voucher_attr_control_create_mach_voucher(
2436 ipc_voucher_attr_control_t control
,
2437 mach_voucher_attr_raw_recipe_array_t recipes
,
2438 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2439 ipc_voucher_t
*new_voucher
)
2441 mach_voucher_attr_key_t control_key
;
2442 mach_voucher_attr_recipe_t sub_recipe
;
2443 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2444 ipc_voucher_t voucher
= IV_NULL
;
2445 kern_return_t kr
= KERN_SUCCESS
;
2447 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
2448 return KERN_INVALID_CAPABILITY
;
2451 /* if nothing to do ... */
2452 if (0 == recipe_size
) {
2453 *new_voucher
= IV_NULL
;
2454 return KERN_SUCCESS
;
2457 /* allocate new voucher */
2458 voucher
= iv_alloc(ivgt_keys_in_use
);
2459 if (IV_NULL
== voucher
) {
2460 return KERN_RESOURCE_SHORTAGE
;
2463 control_key
= iv_index_to_key(control
->ivac_key_index
);
2465 /* iterate over the recipe items */
2466 while (0 < recipe_size
- recipe_used
) {
2467 ipc_voucher_t prev_iv
;
2469 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2470 kr
= KERN_INVALID_ARGUMENT
;
2474 /* find the next recipe */
2475 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2476 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2477 kr
= KERN_INVALID_ARGUMENT
;
2480 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2482 /* convert voucher port name (current space) into a voucher reference */
2483 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2484 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2485 kr
= KERN_INVALID_CAPABILITY
;
2489 kr
= ipc_execute_voucher_recipe_command(voucher
,
2491 sub_recipe
->command
,
2493 sub_recipe
->content
,
2494 sub_recipe
->content_size
,
2495 (sub_recipe
->key
== control_key
));
2496 ipc_voucher_release(prev_iv
);
2498 if (KERN_SUCCESS
!= kr
) {
2503 if (KERN_SUCCESS
== kr
) {
2504 *new_voucher
= iv_dedup(voucher
);
2506 *new_voucher
= IV_NULL
;
2507 iv_dealloc(voucher
, FALSE
);
2513 * Routine: host_create_mach_voucher
2515 * Create a new mach voucher and initialize it by processing the
2516 * supplied recipe(s).
2518 * Comming in from user-space, each recipe item will have a previous
2519 * recipe port name that needs to be converted to a voucher. Because
2520 * we can't rely on the port namespace to hold a reference on each
2521 * previous voucher port for the duration of processing that command,
2522 * we have to convert the name to a voucher reference and release it
2523 * after the command processing is done.
2526 host_create_mach_voucher(
2528 mach_voucher_attr_raw_recipe_array_t recipes
,
2529 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2530 ipc_voucher_t
*new_voucher
)
2532 mach_voucher_attr_recipe_t sub_recipe
;
2533 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2534 ipc_voucher_t voucher
= IV_NULL
;
2535 kern_return_t kr
= KERN_SUCCESS
;
2537 if (host
== HOST_NULL
) {
2538 return KERN_INVALID_ARGUMENT
;
2541 /* if nothing to do ... */
2542 if (0 == recipe_size
) {
2543 *new_voucher
= IV_NULL
;
2544 return KERN_SUCCESS
;
2547 /* allocate new voucher */
2548 voucher
= iv_alloc(ivgt_keys_in_use
);
2549 if (IV_NULL
== voucher
) {
2550 return KERN_RESOURCE_SHORTAGE
;
2553 /* iterate over the recipe items */
2554 while (0 < recipe_size
- recipe_used
) {
2555 ipc_voucher_t prev_iv
;
2557 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2558 kr
= KERN_INVALID_ARGUMENT
;
2562 /* find the next recipe */
2563 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2564 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2565 kr
= KERN_INVALID_ARGUMENT
;
2568 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2570 /* convert voucher port name (current space) into a voucher reference */
2571 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2572 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2573 kr
= KERN_INVALID_CAPABILITY
;
2577 kr
= ipc_execute_voucher_recipe_command(voucher
,
2579 sub_recipe
->command
,
2581 sub_recipe
->content
,
2582 sub_recipe
->content_size
,
2584 ipc_voucher_release(prev_iv
);
2586 if (KERN_SUCCESS
!= kr
) {
2591 if (KERN_SUCCESS
== kr
) {
2592 *new_voucher
= iv_dedup(voucher
);
2594 *new_voucher
= IV_NULL
;
2595 iv_dealloc(voucher
, FALSE
);
2601 * Routine: host_register_well_known_mach_voucher_attr_manager
2603 * Register the user-level resource manager responsible for a given
2606 * The manager port passed in has to be converted/wrapped
2607 * in an ipc_voucher_attr_manager_t structure and then call the
2608 * internal variant. We have a generic ipc voucher manager
2609 * type that implements a MIG proxy out to user-space just for
2613 host_register_well_known_mach_voucher_attr_manager(
2615 mach_voucher_attr_manager_t __unused manager
,
2616 mach_voucher_attr_value_handle_t __unused default_value
,
2617 mach_voucher_attr_key_t __unused key
,
2618 ipc_voucher_attr_control_t __unused
*control
)
2620 if (HOST_NULL
== host
) {
2621 return KERN_INVALID_HOST
;
2625 return KERN_NOT_SUPPORTED
;
2628 * Allocate a mig_voucher_attr_manager_t that provides the
2629 * MIG proxy functions for the three manager callbacks and
2630 * store the port right in there.
2632 * If the user-space manager dies, we'll detect it on our
2633 * next upcall, and cleanup the proxy at that point.
2635 mig_voucher_attr_manager_t proxy
;
2638 proxy
= mvam_alloc(manager
);
2640 kr
= ipc_register_well_known_mach_voucher_attr_manager(&proxy
->mvam_manager
,
2644 if (KERN_SUCCESS
!= kr
) {
2645 mvam_release(proxy
);
2653 * Routine: host_register_mach_voucher_attr_manager
2655 * Register the user-space resource manager and return a
2656 * dynamically allocated key.
2658 * Wrap the supplied port with the MIG proxy ipc
2659 * voucher resource manager, and then call the internal
2663 host_register_mach_voucher_attr_manager(
2665 mach_voucher_attr_manager_t __unused manager
,
2666 mach_voucher_attr_value_handle_t __unused default_value
,
2667 mach_voucher_attr_key_t __unused
*key
,
2668 ipc_voucher_attr_control_t __unused
*control
)
2670 if (HOST_NULL
== host
) {
2671 return KERN_INVALID_HOST
;
2674 return KERN_NOT_SUPPORTED
;
2678 * Routine: ipc_get_pthpriority_from_kmsg_voucher
2680 * Get the canonicalized pthread priority from the voucher attached in the kmsg.
2683 ipc_get_pthpriority_from_kmsg_voucher(
2685 ipc_pthread_priority_value_t
*canonicalize_priority_value
)
2687 ipc_voucher_t pthread_priority_voucher
;
2688 mach_voucher_attr_raw_recipe_size_t content_size
=
2689 sizeof(mach_voucher_attr_recipe_data_t
) + sizeof(ipc_pthread_priority_value_t
);
2690 uint8_t content_data
[content_size
];
2691 mach_voucher_attr_recipe_t cur_content
;
2692 kern_return_t kr
= KERN_SUCCESS
;
2694 if (!IP_VALID(kmsg
->ikm_voucher
)) {
2695 return KERN_FAILURE
;
2698 pthread_priority_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2699 kr
= mach_voucher_extract_attr_recipe(pthread_priority_voucher
,
2700 MACH_VOUCHER_ATTR_KEY_PTHPRIORITY
,
2703 if (kr
!= KERN_SUCCESS
) {
2707 /* return KERN_INVALID_VALUE for default value */
2708 if (content_size
< sizeof(mach_voucher_attr_recipe_t
)) {
2709 return KERN_INVALID_VALUE
;
2712 cur_content
= (mach_voucher_attr_recipe_t
) (void *) &content_data
[0];
2713 assert(cur_content
->content_size
== sizeof(ipc_pthread_priority_value_t
));
2714 memcpy(canonicalize_priority_value
, cur_content
->content
, sizeof(ipc_pthread_priority_value_t
));
2716 return KERN_SUCCESS
;
2721 * Routine: ipc_voucher_send_preprocessing
2723 * Processing of the voucher in the kmsg before sending it.
2724 * Currently use to switch PERSONA_TOKEN in case of process with
2725 * no com.apple.private.personas.propagate entitlement.
2728 ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg
)
2730 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2731 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2732 sizeof(ipc_voucher_attr_recipe_data_t
);
2733 ipc_voucher_t pre_processed_voucher
;
2734 ipc_voucher_t voucher_to_send
;
2736 int need_preprocessing
= FALSE
;
2738 if (!IP_VALID(kmsg
->ikm_voucher
) || current_task() == kernel_task
) {
2742 /* setup recipe for preprocessing of all the attributes. */
2743 pre_processed_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2745 kr
= ipc_voucher_prepare_processing_recipe(pre_processed_voucher
,
2746 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2747 &recipe_size
, MACH_VOUCHER_ATTR_SEND_PREPROCESS
,
2748 IVAM_FLAGS_SUPPORT_SEND_PREPROCESS
, &need_preprocessing
);
2750 assert(KERN_SUCCESS
== kr
);
2752 * Only do send preprocessing if the voucher needs any pre processing.
2754 if (need_preprocessing
) {
2755 kr
= ipc_create_mach_voucher(recipes
,
2758 assert(KERN_SUCCESS
== kr
);
2759 ipc_port_release_send(kmsg
->ikm_voucher
);
2760 kmsg
->ikm_voucher
= convert_voucher_to_port(voucher_to_send
);
2765 * Routine: ipc_voucher_receive_postprocessing
2767 * Redeems the voucher attached to the kmsg.
2769 * Although it is possible to call ipc_importance_receive
2770 * here, it is called in mach_msg_receive_results and not here
2771 * in order to maintain symmetry with ipc_voucher_send_preprocessing.
2774 ipc_voucher_receive_postprocessing(
2776 mach_msg_option_t option
)
2778 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2779 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2780 sizeof(ipc_voucher_attr_recipe_data_t
);
2781 ipc_voucher_t recv_voucher
;
2782 ipc_voucher_t sent_voucher
;
2784 int need_postprocessing
= FALSE
;
2786 if ((option
& MACH_RCV_VOUCHER
) == 0 || (!IP_VALID(kmsg
->ikm_voucher
)) ||
2787 current_task() == kernel_task
) {
2791 /* setup recipe for auto redeem of all the attributes. */
2792 sent_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2794 kr
= ipc_voucher_prepare_processing_recipe(sent_voucher
,
2795 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2796 &recipe_size
, MACH_VOUCHER_ATTR_AUTO_REDEEM
,
2797 IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS
, &need_postprocessing
);
2799 assert(KERN_SUCCESS
== kr
);
2802 * Only do receive postprocessing if the voucher needs any post processing.
2804 if (need_postprocessing
) {
2805 kr
= ipc_create_mach_voucher(recipes
,
2808 assert(KERN_SUCCESS
== kr
);
2809 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
2810 kmsg
->ikm_header
->msgh_bits
|= (MACH_MSG_TYPE_MOVE_SEND
<< 16);
2811 ipc_port_release_send(kmsg
->ikm_voucher
);
2812 kmsg
->ikm_voucher
= convert_voucher_to_port(recv_voucher
);
2817 * Routine: ipc_voucher_prepare_processing_recipe
2819 * Check if the given voucher has an attribute which supports
2820 * the given flag and prepare a recipe to apply that supported
2823 static kern_return_t
2824 ipc_voucher_prepare_processing_recipe(
2825 ipc_voucher_t voucher
,
2826 ipc_voucher_attr_raw_recipe_array_t recipes
,
2827 ipc_voucher_attr_raw_recipe_array_size_t
*in_out_size
,
2828 mach_voucher_attr_recipe_command_t command
,
2829 ipc_voucher_attr_manager_flags flags
,
2830 int *need_processing
)
2832 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= *in_out_size
;
2833 ipc_voucher_attr_raw_recipe_array_size_t recipe_used
= 0;
2834 iv_index_t key_index
;
2835 ipc_voucher_attr_recipe_t recipe
;
2837 if (IV_NULL
== voucher
) {
2838 return KERN_INVALID_ARGUMENT
;
2841 /* Setup a recipe to copy all attributes. */
2842 if (recipe_size
< sizeof(*recipe
)) {
2843 return KERN_NO_SPACE
;
2846 *need_processing
= FALSE
;
2847 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2848 recipe
->key
= MACH_VOUCHER_ATTR_KEY_ALL
;
2849 recipe
->command
= MACH_VOUCHER_ATTR_COPY
;
2850 recipe
->previous_voucher
= voucher
;
2851 recipe
->content_size
= 0;
2852 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2854 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2855 ipc_voucher_attr_manager_t manager
;
2856 mach_voucher_attr_key_t key
;
2857 iv_index_t value_index
;
2859 /* don't output anything for a default value */
2860 value_index
= iv_lookup(voucher
, key_index
);
2861 if (IV_UNUSED_VALINDEX
== value_index
) {
2865 if (recipe_size
- recipe_used
< sizeof(*recipe
)) {
2866 return KERN_NO_SPACE
;
2869 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2872 * Get the manager for this key_index. The
2873 * existence of a non-default value for this
2874 * slot within our voucher will keep the
2875 * manager referenced during the callout.
2877 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2878 assert(IVAM_NULL
!= manager
);
2879 if (IVAM_NULL
== manager
) {
2883 /* Check if the supported flag is set in the manager */
2884 if ((manager
->ivam_flags
& flags
) == 0) {
2888 key
= iv_index_to_key(key_index
);
2891 recipe
->command
= command
;
2892 recipe
->content_size
= 0;
2893 recipe
->previous_voucher
= voucher
;
2895 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2896 *need_processing
= TRUE
;
2899 *in_out_size
= recipe_used
;
2900 return KERN_SUCCESS
;
2904 * Activity id Generation
2906 uint64_t voucher_activity_id
;
2908 #define generate_activity_id(x) \
2909 ((uint64_t)OSAddAtomic64((x), (int64_t *)&voucher_activity_id))
2912 * Routine: mach_init_activity_id
2914 * Initialize voucher activity id.
2917 mach_init_activity_id(void)
2919 voucher_activity_id
= 1;
2923 * Routine: mach_generate_activity_id
2925 * Generate a system wide voucher activity id.
2928 mach_generate_activity_id(
2929 struct mach_generate_activity_id_args
*args
)
2931 uint64_t activity_id
;
2932 kern_return_t kr
= KERN_SUCCESS
;
2934 if (args
->count
<= 0 || args
->count
> MACH_ACTIVITY_ID_COUNT_MAX
) {
2935 return KERN_INVALID_ARGUMENT
;
2938 activity_id
= generate_activity_id(args
->count
);
2939 kr
= copyout(&activity_id
, args
->activity_id
, sizeof(activity_id
));
2944 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2947 * Build-in a simple User Data Resource Manager
2949 #define USER_DATA_MAX_DATA (16*1024)
2951 struct user_data_value_element
{
2952 mach_voucher_attr_value_reference_t e_made
;
2953 mach_voucher_attr_content_size_t e_size
;
2956 queue_chain_t e_hash_link
;
2960 typedef struct user_data_value_element
*user_data_element_t
;
2963 * User Data Voucher Hash Table
2965 #define USER_DATA_HASH_BUCKETS 127
2966 #define USER_DATA_HASH_BUCKET(x) ((x) % USER_DATA_HASH_BUCKETS)
2968 static queue_head_t user_data_bucket
[USER_DATA_HASH_BUCKETS
];
2969 static lck_spin_t user_data_lock_data
;
2971 #define user_data_lock_init() \
2972 lck_spin_init(&user_data_lock_data, &ipc_lck_grp, &ipc_lck_attr)
2973 #define user_data_lock_destroy() \
2974 lck_spin_destroy(&user_data_lock_data, &ipc_lck_grp)
2975 #define user_data_lock() \
2976 lck_spin_lock_grp(&user_data_lock_data, &ipc_lck_grp)
2977 #define user_data_lock_try() \
2978 lck_spin_try_lock_grp(&user_data_lock_data, &ipc_lck_grp)
2979 #define user_data_unlock() \
2980 lck_spin_unlock(&user_data_lock_data)
2982 static kern_return_t
2983 user_data_release_value(
2984 ipc_voucher_attr_manager_t manager
,
2985 mach_voucher_attr_key_t key
,
2986 mach_voucher_attr_value_handle_t value
,
2987 mach_voucher_attr_value_reference_t sync
);
2989 static kern_return_t
2990 user_data_get_value(
2991 ipc_voucher_attr_manager_t manager
,
2992 mach_voucher_attr_key_t key
,
2993 mach_voucher_attr_recipe_command_t command
,
2994 mach_voucher_attr_value_handle_array_t prev_values
,
2995 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
2996 mach_voucher_attr_content_t content
,
2997 mach_voucher_attr_content_size_t content_size
,
2998 mach_voucher_attr_value_handle_t
*out_value
,
2999 mach_voucher_attr_value_flags_t
*out_flags
,
3000 ipc_voucher_t
*out_value_voucher
);
3002 static kern_return_t
3003 user_data_extract_content(
3004 ipc_voucher_attr_manager_t manager
,
3005 mach_voucher_attr_key_t key
,
3006 mach_voucher_attr_value_handle_array_t values
,
3007 mach_voucher_attr_value_handle_array_size_t value_count
,
3008 mach_voucher_attr_recipe_command_t
*out_command
,
3009 mach_voucher_attr_content_t out_content
,
3010 mach_voucher_attr_content_size_t
*in_out_content_size
);
3012 static kern_return_t
3014 ipc_voucher_attr_manager_t manager
,
3015 mach_voucher_attr_key_t key
,
3016 mach_voucher_attr_value_handle_array_t values
,
3017 mach_msg_type_number_t value_count
,
3018 mach_voucher_attr_command_t command
,
3019 mach_voucher_attr_content_t in_content
,
3020 mach_voucher_attr_content_size_t in_content_size
,
3021 mach_voucher_attr_content_t out_content
,
3022 mach_voucher_attr_content_size_t
*out_content_size
);
3026 ipc_voucher_attr_manager_t manager
);
3028 struct ipc_voucher_attr_manager user_data_manager
= {
3029 .ivam_release_value
= user_data_release_value
,
3030 .ivam_get_value
= user_data_get_value
,
3031 .ivam_extract_content
= user_data_extract_content
,
3032 .ivam_command
= user_data_command
,
3033 .ivam_release
= user_data_release
,
3034 .ivam_flags
= IVAM_FLAGS_NONE
,
3037 ipc_voucher_attr_control_t user_data_control
;
3038 ipc_voucher_attr_control_t test_control
;
3040 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) && defined(MACH_VOUCHER_ATTR_KEY_TEST)
3041 #define USER_DATA_ASSERT_KEY(key) \
3042 assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key) || \
3043 MACH_VOUCHER_ATTR_KEY_TEST == (key));
3044 #elif defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
3045 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key))
3047 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_TEST == (key))
3051 * Routine: user_data_release_value
3053 * Release a made reference on a specific value managed by
3054 * this voucher attribute manager.
3056 * Must remove the element associated with this value from
3057 * the hash if this is the last know made reference.
3059 static kern_return_t
3060 user_data_release_value(
3061 ipc_voucher_attr_manager_t __assert_only manager
,
3062 mach_voucher_attr_key_t __assert_only key
,
3063 mach_voucher_attr_value_handle_t value
,
3064 mach_voucher_attr_value_reference_t sync
)
3066 user_data_element_t elem
;
3069 assert(&user_data_manager
== manager
);
3070 USER_DATA_ASSERT_KEY(key
);
3072 elem
= (user_data_element_t
)value
;
3073 hash
= elem
->e_hash
;
3076 if (sync
== elem
->e_made
) {
3077 queue_remove(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
);
3079 kfree(elem
, sizeof(*elem
) + elem
->e_size
);
3080 return KERN_SUCCESS
;
3082 assert(sync
< elem
->e_made
);
3085 return KERN_FAILURE
;
3089 * Routine: user_data_checksum
3091 * Provide a rudimentary checksum for the data presented
3092 * to these voucher attribute managers.
3096 mach_voucher_attr_content_t content
,
3097 mach_voucher_attr_content_size_t content_size
)
3099 mach_voucher_attr_content_size_t i
;
3100 iv_index_t cksum
= 0;
3102 for (i
= 0; i
< content_size
; i
++, content
++) {
3103 cksum
= (cksum
<< 8) ^ (cksum
+ *(unsigned char *)content
);
3110 * Routine: user_data_dedup
3112 * See if the content represented by this request already exists
3113 * in another user data element. If so return a made reference
3114 * to the existing element. Otherwise, create a new element and
3115 * return that (after inserting it in the hash).
3119 * A made reference on the user_data_element_t
3121 static user_data_element_t
3123 mach_voucher_attr_content_t content
,
3124 mach_voucher_attr_content_size_t content_size
)
3128 user_data_element_t elem
;
3129 user_data_element_t alloc
= NULL
;
3131 sum
= user_data_checksum(content
, content_size
);
3132 hash
= USER_DATA_HASH_BUCKET(sum
);
3136 queue_iterate(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
) {
3137 assert(elem
->e_hash
== hash
);
3139 /* if sums match... */
3140 if (elem
->e_sum
== sum
&& elem
->e_size
== content_size
) {
3143 /* and all data matches */
3144 for (i
= 0; i
< content_size
; i
++) {
3145 if (elem
->e_data
[i
] != content
[i
]) {
3149 if (i
< content_size
) {
3153 /* ... we found a match... */
3158 if (NULL
!= alloc
) {
3159 kfree(alloc
, sizeof(*alloc
) + content_size
);
3166 if (NULL
== alloc
) {
3169 alloc
= (user_data_element_t
)kalloc(sizeof(*alloc
) + content_size
);
3171 alloc
->e_size
= content_size
;
3173 alloc
->e_hash
= hash
;
3174 memcpy(alloc
->e_data
, content
, content_size
);
3178 queue_enter(&user_data_bucket
[hash
], alloc
, user_data_element_t
, e_hash_link
);
3184 static kern_return_t
3185 user_data_get_value(
3186 ipc_voucher_attr_manager_t __assert_only manager
,
3187 mach_voucher_attr_key_t __assert_only key
,
3188 mach_voucher_attr_recipe_command_t command
,
3189 mach_voucher_attr_value_handle_array_t prev_values
,
3190 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3191 mach_voucher_attr_content_t content
,
3192 mach_voucher_attr_content_size_t content_size
,
3193 mach_voucher_attr_value_handle_t
*out_value
,
3194 mach_voucher_attr_value_flags_t
*out_flags
,
3195 ipc_voucher_t
*out_value_voucher
)
3197 user_data_element_t elem
;
3199 assert(&user_data_manager
== manager
);
3200 USER_DATA_ASSERT_KEY(key
);
3202 /* never an out voucher */
3203 *out_value_voucher
= IPC_VOUCHER_NULL
;
3204 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
3207 case MACH_VOUCHER_ATTR_REDEEM
:
3209 /* redeem of previous values is the value */
3210 if (0 < prev_value_count
) {
3211 elem
= (user_data_element_t
)prev_values
[0];
3212 assert(0 < elem
->e_made
);
3214 *out_value
= prev_values
[0];
3215 return KERN_SUCCESS
;
3218 /* redeem of default is default */
3220 return KERN_SUCCESS
;
3222 case MACH_VOUCHER_ATTR_USER_DATA_STORE
:
3223 if (USER_DATA_MAX_DATA
< content_size
) {
3224 return KERN_RESOURCE_SHORTAGE
;
3227 /* empty is the default */
3228 if (0 == content_size
) {
3230 return KERN_SUCCESS
;
3233 elem
= user_data_dedup(content
, content_size
);
3234 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
3235 return KERN_SUCCESS
;
3238 /* every other command is unknown */
3239 return KERN_INVALID_ARGUMENT
;
3243 static kern_return_t
3244 user_data_extract_content(
3245 ipc_voucher_attr_manager_t __assert_only manager
,
3246 mach_voucher_attr_key_t __assert_only key
,
3247 mach_voucher_attr_value_handle_array_t values
,
3248 mach_voucher_attr_value_handle_array_size_t value_count
,
3249 mach_voucher_attr_recipe_command_t
*out_command
,
3250 mach_voucher_attr_content_t out_content
,
3251 mach_voucher_attr_content_size_t
*in_out_content_size
)
3253 mach_voucher_attr_content_size_t size
= 0;
3254 user_data_element_t elem
;
3257 assert(&user_data_manager
== manager
);
3258 USER_DATA_ASSERT_KEY(key
);
3260 /* concatenate the stored data items */
3261 for (i
= 0; i
< value_count
&& *in_out_content_size
> 0; i
++) {
3262 elem
= (user_data_element_t
)values
[i
];
3263 assert(USER_DATA_MAX_DATA
>= elem
->e_size
);
3265 if (size
+ elem
->e_size
> *in_out_content_size
) {
3266 return KERN_NO_SPACE
;
3269 memcpy(&out_content
[size
], elem
->e_data
, elem
->e_size
);
3270 size
+= elem
->e_size
;
3272 *out_command
= MACH_VOUCHER_ATTR_BITS_STORE
;
3273 *in_out_content_size
= size
;
3274 return KERN_SUCCESS
;
3277 static kern_return_t
3279 ipc_voucher_attr_manager_t __assert_only manager
,
3280 mach_voucher_attr_key_t __assert_only key
,
3281 mach_voucher_attr_value_handle_array_t __unused values
,
3282 mach_msg_type_number_t __unused value_count
,
3283 mach_voucher_attr_command_t __unused command
,
3284 mach_voucher_attr_content_t __unused in_content
,
3285 mach_voucher_attr_content_size_t __unused in_content_size
,
3286 mach_voucher_attr_content_t __unused out_content
,
3287 mach_voucher_attr_content_size_t __unused
*out_content_size
)
3289 assert(&user_data_manager
== manager
);
3290 USER_DATA_ASSERT_KEY(key
);
3291 return KERN_FAILURE
;
3296 ipc_voucher_attr_manager_t manager
)
3298 if (manager
!= &user_data_manager
) {
3302 panic("Voucher user-data manager released");
3305 static int user_data_manager_inited
= 0;
3308 user_data_attr_manager_init()
3312 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
3313 if ((user_data_manager_inited
& 0x1) != 0x1) {
3314 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3315 (mach_voucher_attr_value_handle_t
)0,
3316 MACH_VOUCHER_ATTR_KEY_USER_DATA
,
3317 &user_data_control
);
3318 if (KERN_SUCCESS
!= kr
) {
3319 printf("Voucher user-data manager register(USER-DATA) returned %d", kr
);
3321 user_data_manager_inited
|= 0x1;
3325 #if defined(MACH_VOUCHER_ATTR_KEY_TEST)
3326 if ((user_data_manager_inited
& 0x2) != 0x2) {
3327 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3328 (mach_voucher_attr_value_handle_t
)0,
3329 MACH_VOUCHER_ATTR_KEY_TEST
,
3331 if (KERN_SUCCESS
!= kr
) {
3332 printf("Voucher user-data manager register(TEST) returned %d", kr
);
3334 user_data_manager_inited
|= 0x2;
3338 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
3341 for (i
= 0; i
< USER_DATA_HASH_BUCKETS
; i
++) {
3342 queue_init(&user_data_bucket
[i
]);
3345 user_data_lock_init();
3349 #endif /* MACH_DEBUG */