2 * Copyright (c) 2013-2020 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 SECURITY_READ_ONLY_LATE(zone_t
) ipc_voucher_zone
;
54 static ZONE_DECLARE(ipc_voucher_attr_control_zone
, "ipc voucher attr controls",
55 sizeof(struct ipc_voucher_attr_control
), ZC_NOENCRYPT
| ZC_ZFREE_CLEARMEM
);
57 ZONE_INIT(&ipc_voucher_zone
, "ipc vouchers", sizeof(struct ipc_voucher
),
58 ZC_NOENCRYPT
| ZC_ZFREE_CLEARMEM
| ZC_NOSEQUESTER
,
59 ZONE_ID_IPC_VOUCHERS
, NULL
);
61 #define voucher_require(v) \
62 zone_id_require(ZONE_ID_IPC_VOUCHERS, sizeof(struct ipc_voucher), v)
67 #define IV_HASH_BUCKETS 127
68 #define IV_HASH_BUCKET(x) ((x) % IV_HASH_BUCKETS)
70 static queue_head_t ivht_bucket
[IV_HASH_BUCKETS
];
71 static LCK_SPIN_DECLARE_ATTR(ivht_lock_data
, &ipc_lck_grp
, &ipc_lck_attr
);
72 static uint32_t ivht_count
= 0;
74 #define ivht_lock_destroy() \
75 lck_spin_destroy(&ivht_lock_data, &ipc_lck_grp)
77 lck_spin_lock_grp(&ivht_lock_data, &ipc_lck_grp)
78 #define ivht_lock_try() \
79 lck_spin_try_lock_grp(&ivht_lock_data, &ipc_lck_grp)
80 #define ivht_unlock() \
81 lck_spin_unlock(&ivht_lock_data)
84 * Global table of resource manager registrations
86 * NOTE: For now, limited to well-known resource managers
87 * eventually, will include dynamic allocations requiring
88 * table growth and hashing by key.
90 static iv_index_t ivgt_keys_in_use
= MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
;
91 static ipc_voucher_global_table_element iv_global_table
[MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
];
92 static LCK_SPIN_DECLARE_ATTR(ivgt_lock_data
, &ipc_lck_grp
, &ipc_lck_attr
);
94 #define ivgt_lock_destroy() \
95 lck_spin_destroy(&ivgt_lock_data, &ipc_lck_grp)
97 lck_spin_lock_grp(&ivgt_lock_data, &ipc_lck_grp)
98 #define ivgt_lock_try() \
99 lck_spin_try_lock_grp(&ivgt_lock_data, &ipc_lck_grp)
100 #define ivgt_unlock() \
101 lck_spin_unlock(&ivgt_lock_data)
103 ipc_voucher_t
iv_alloc(iv_index_t entries
);
104 void iv_dealloc(ipc_voucher_t iv
, boolean_t unhash
);
106 os_refgrp_decl(static, iv_refgrp
, "voucher", NULL
);
107 os_refgrp_decl(static, ivac_refgrp
, "voucher attribute control", NULL
);
110 iv_reference(ipc_voucher_t iv
)
112 os_ref_retain(&iv
->iv_refs
);
116 iv_release(ipc_voucher_t iv
)
118 if (os_ref_release(&iv
->iv_refs
) == 0) {
119 iv_dealloc(iv
, TRUE
);
124 * freelist helper macros
126 #define IV_FREELIST_END ((iv_index_t) 0)
129 * Attribute value hashing helper macros
131 #define IV_HASH_END UINT32_MAX
132 #define IV_HASH_VAL(sz, val) \
133 (((val) >> 3) % (sz))
135 static inline iv_index_t
137 iv_index_t key_index
,
138 mach_voucher_attr_value_handle_t value
)
140 ipc_voucher_attr_control_t ivac
;
142 ivac
= iv_global_table
[key_index
].ivgte_control
;
143 assert(IVAC_NULL
!= ivac
);
144 return IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
148 * Convert a key to an index. This key-index is used to both index
149 * into the voucher table of attribute cache indexes and also the
150 * table of resource managers by key.
152 * For now, well-known keys have a one-to-one mapping of indexes
153 * into these tables. But as time goes on, that may not always
154 * be the case (sparse use over time). This isolates the code from
155 * having to change in these cases - yet still lets us keep a densely
156 * packed set of tables.
158 static inline iv_index_t
159 iv_key_to_index(mach_voucher_attr_key_t key
)
161 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
||
162 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
< key
) {
163 return IV_UNUSED_KEYINDEX
;
165 return (iv_index_t
)key
- 1;
168 static inline mach_voucher_attr_key_t
169 iv_index_to_key(iv_index_t key_index
)
171 if (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
> key_index
) {
172 return iv_global_table
[key_index
].ivgte_key
;
174 return MACH_VOUCHER_ATTR_KEY_NONE
;
177 static void ivace_release(iv_index_t key_index
, iv_index_t value_index
);
178 static void ivace_lookup_values(iv_index_t key_index
, iv_index_t value_index
,
179 mach_voucher_attr_value_handle_array_t values
,
180 mach_voucher_attr_value_handle_array_size_t
*count
);
182 static iv_index_t
iv_lookup(ipc_voucher_t
, iv_index_t
);
185 static void ivgt_lookup(iv_index_t
,
187 ipc_voucher_attr_manager_t
*,
188 ipc_voucher_attr_control_t
*);
191 ipc_voucher_prepare_processing_recipe(
192 ipc_voucher_t voucher
,
193 ipc_voucher_attr_raw_recipe_array_t recipes
,
194 ipc_voucher_attr_raw_recipe_array_size_t
*in_out_size
,
195 mach_voucher_attr_recipe_command_t command
,
196 ipc_voucher_attr_manager_flags flags
,
197 int *need_processing
);
201 ipc_voucher_init(void)
203 /* initialize voucher hash */
204 for (iv_index_t i
= 0; i
< IV_HASH_BUCKETS
; i
++) {
205 queue_init(&ivht_bucket
[i
]);
208 STARTUP(MACH_IPC
, STARTUP_RANK_FIRST
, ipc_voucher_init
);
211 iv_alloc(iv_index_t entries
)
217 iv
= (ipc_voucher_t
)zalloc(ipc_voucher_zone
);
222 os_ref_init(&iv
->iv_refs
, &iv_refgrp
);
225 iv
->iv_port
= IP_NULL
;
227 if (entries
> IV_ENTRIES_INLINE
) {
230 /* TODO - switch to ipc_table method of allocation */
231 table
= (iv_entry_t
) kalloc(sizeof(*table
) * entries
);
232 if (IVE_NULL
== table
) {
233 zfree(ipc_voucher_zone
, iv
);
236 iv
->iv_table
= table
;
237 iv
->iv_table_size
= entries
;
239 iv
->iv_table
= iv
->iv_inline_table
;
240 iv
->iv_table_size
= IV_ENTRIES_INLINE
;
243 /* initialize the table entries */
244 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
245 iv
->iv_table
[i
] = IV_UNUSED_VALINDEX
;
254 * Set the voucher's value index for a given key index.
256 * This is only called during voucher creation, as
257 * they are immutable once references are distributed.
260 iv_set(ipc_voucher_t iv
,
261 iv_index_t key_index
,
262 iv_index_t value_index
)
264 assert(key_index
< iv
->iv_table_size
);
265 iv
->iv_table
[key_index
] = value_index
;
269 iv_dealloc(ipc_voucher_t iv
, boolean_t unhash
)
271 ipc_port_t port
= iv
->iv_port
;
275 * Do we have to remove it from the hash?
279 assert(os_ref_get_count(&iv
->iv_refs
) == 0);
280 assert(IV_HASH_BUCKETS
> iv
->iv_hash
);
281 queue_remove(&ivht_bucket
[iv
->iv_hash
], iv
, ipc_voucher_t
, iv_hash_link
);
285 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_DESTROY
) | DBG_FUNC_NONE
,
286 VM_KERNEL_ADDRPERM((uintptr_t)iv
), 0, ivht_count
, 0, 0);
288 os_ref_count_t cnt __assert_only
= os_ref_release(&iv
->iv_refs
);
293 * if a port was allocated for this voucher,
294 * it must not have any remaining send rights,
295 * because the port's reference on the voucher
296 * is gone. We can just discard it now.
298 if (IP_VALID(port
)) {
299 require_ip_active(port
);
300 assert(port
->ip_srights
== 0);
302 ipc_port_dealloc_kernel(port
);
305 /* release the attribute references held by this voucher */
306 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
307 ivace_release(i
, iv
->iv_table
[i
]);
313 if (iv
->iv_table
!= iv
->iv_inline_table
) {
315 iv
->iv_table_size
* sizeof(*iv
->iv_table
));
318 zfree(ipc_voucher_zone
, iv
);
324 * Find the voucher's value index for a given key_index
326 * Vouchers are immutable, so no locking required to do
329 static inline iv_index_t
330 iv_lookup(ipc_voucher_t iv
, iv_index_t key_index
)
332 if (key_index
< iv
->iv_table_size
) {
333 return iv
->iv_table
[key_index
];
335 return IV_UNUSED_VALINDEX
;
339 * Routine: unsafe_convert_port_to_voucher
341 * Unsafe conversion of a port to a voucher.
342 * Intended only for use by trace and debugging
343 * code. Consumes nothing, validates very little,
344 * produces an unreferenced voucher, which you
345 * MAY NOT use as a voucher, only log as an
348 * Caller has a send-right reference to port.
349 * Port may or may not be locked.
352 unsafe_convert_port_to_voucher(
355 if (IP_VALID(port
)) {
356 /* vouchers never labeled (they get transformed before use) */
357 if (ip_is_kolabeled(port
)) {
358 return (uintptr_t)IV_NULL
;
362 * No need to lock because we have a reference on the
363 * port, and if it is a true voucher port, that reference
364 * keeps the voucher bound to the port (and active).
366 if (ip_kotype(port
) == IKOT_VOUCHER
) {
367 return (uintptr_t)port
->ip_kobject
;
370 return (uintptr_t)IV_NULL
;
374 ip_get_voucher(ipc_port_t port
)
376 ipc_voucher_t voucher
= (ipc_voucher_t
)ip_get_kobject(port
);
377 require_ip_active(port
);
378 voucher_require(voucher
);
383 * Routine: convert_port_to_voucher
385 * Convert from a port to a voucher.
386 * Doesn't consume the port [send-right] ref;
387 * produces a voucher ref, which may be null.
389 * Caller has a send-right reference to port.
390 * Port may or may not be locked.
393 convert_port_to_voucher(
396 if (IP_VALID(port
) && ip_kotype(port
) == IKOT_VOUCHER
) {
398 * No need to lock because we have a reference on the
399 * port, and if it is a true voucher port, that reference
400 * keeps the voucher bound to the port (and active).
402 ipc_voucher_t voucher
= ip_get_voucher(port
);
403 ipc_voucher_reference(voucher
);
410 * Routine: convert_port_name_to_voucher
412 * Convert from a port name in the current space to a voucher.
413 * Produces a voucher ref, which may be null.
419 convert_port_name_to_voucher(
420 mach_port_name_t voucher_name
)
426 if (MACH_PORT_VALID(voucher_name
)) {
427 kr
= ipc_port_translate_send(current_space(), voucher_name
, &port
);
428 if (KERN_SUCCESS
!= kr
) {
432 iv
= convert_port_to_voucher(port
);
441 ipc_voucher_reference(ipc_voucher_t voucher
)
443 if (IPC_VOUCHER_NULL
== voucher
) {
447 iv_reference(voucher
);
451 ipc_voucher_release(ipc_voucher_t voucher
)
453 if (IPC_VOUCHER_NULL
!= voucher
) {
459 * Routine: ipc_voucher_notify
461 * Called whenever the Mach port system detects no-senders
462 * on the voucher port.
465 ipc_voucher_notify(mach_msg_header_t
*msg
)
467 mach_no_senders_notification_t
*notification
= (void *)msg
;
468 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
469 ipc_voucher_t voucher
= ip_get_voucher(port
);
471 assert(IKOT_VOUCHER
== ip_kotype(port
));
473 /* consume the reference donated by convert_voucher_to_port */
474 ipc_voucher_release(voucher
);
478 * Convert a voucher to a port.
481 convert_voucher_to_port(ipc_voucher_t voucher
)
483 if (IV_NULL
== voucher
) {
487 voucher_require(voucher
);
488 assert(os_ref_get_count(&voucher
->iv_refs
) > 0);
491 * make a send right and donate our reference for ipc_voucher_notify
492 * if this is the first send right
494 if (!ipc_kobject_make_send_lazy_alloc_port(&voucher
->iv_port
,
495 (ipc_kobject_t
)voucher
, IKOT_VOUCHER
, false, 0)) {
496 ipc_voucher_release(voucher
);
498 return voucher
->iv_port
;
501 #define ivace_reset_data(ivace_elem, next_index) { \
502 (ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE; \
503 (ivace_elem)->ivace_refs = 0; \
504 (ivace_elem)->ivace_persist = 0; \
505 (ivace_elem)->ivace_made = 0; \
506 (ivace_elem)->ivace_free = TRUE; \
507 (ivace_elem)->ivace_releasing = FALSE; \
508 (ivace_elem)->ivace_layered = 0; \
509 (ivace_elem)->ivace_index = IV_HASH_END; \
510 (ivace_elem)->ivace_next = (next_index); \
513 #define ivace_copy_data(ivace_src_elem, ivace_dst_elem) { \
514 (ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
515 (ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs; \
516 (ivace_dst_elem)->ivace_persist = (ivace_src_elem)->ivace_persist; \
517 (ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made; \
518 (ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free; \
519 (ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered; \
520 (ivace_dst_elem)->ivace_releasing = (ivace_src_elem)->ivace_releasing; \
521 (ivace_dst_elem)->ivace_index = (ivace_src_elem)->ivace_index; \
522 (ivace_dst_elem)->ivace_next = (ivace_src_elem)->ivace_next; \
525 ipc_voucher_attr_control_t
526 ivac_alloc(iv_index_t key_index
)
528 ipc_voucher_attr_control_t ivac
;
533 ivac
= (ipc_voucher_attr_control_t
)zalloc(ipc_voucher_attr_control_zone
);
534 if (IVAC_NULL
== ivac
) {
538 os_ref_init(&ivac
->ivac_refs
, &ivac_refgrp
);
539 ivac
->ivac_is_growing
= FALSE
;
540 ivac
->ivac_port
= IP_NULL
;
542 /* start with just the inline table */
543 table
= (ivac_entry_t
) kalloc(IVAC_ENTRIES_MIN
* sizeof(ivac_entry
));
544 ivac
->ivac_table
= table
;
545 ivac
->ivac_table_size
= IVAC_ENTRIES_MIN
;
546 ivac
->ivac_init_table_size
= IVAC_ENTRIES_MIN
;
547 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
548 ivace_reset_data(&table
[i
], i
+ 1);
551 /* the default table entry is never on freelist */
552 table
[0].ivace_next
= IV_HASH_END
;
553 table
[0].ivace_free
= FALSE
;
554 table
[i
- 1].ivace_next
= IV_FREELIST_END
;
555 ivac
->ivac_freelist
= 1;
556 ivac_lock_init(ivac
);
557 ivac
->ivac_key_index
= key_index
;
563 ivac_dealloc(ipc_voucher_attr_control_t ivac
)
565 ipc_voucher_attr_manager_t ivam
= IVAM_NULL
;
566 iv_index_t key_index
= ivac
->ivac_key_index
;
567 ipc_port_t port
= ivac
->ivac_port
;
571 * If the control is in the global table, we
572 * have to remove it from there before we (re)confirm
573 * that the reference count is still zero.
576 if (os_ref_get_count(&ivac
->ivac_refs
) > 0) {
581 /* take it out of the global table */
582 if (iv_global_table
[key_index
].ivgte_control
== ivac
) {
583 ivam
= iv_global_table
[key_index
].ivgte_manager
;
584 iv_global_table
[key_index
].ivgte_manager
= IVAM_NULL
;
585 iv_global_table
[key_index
].ivgte_control
= IVAC_NULL
;
586 iv_global_table
[key_index
].ivgte_key
= MACH_VOUCHER_ATTR_KEY_NONE
;
590 /* release the reference held on the resource manager */
591 if (IVAM_NULL
!= ivam
) {
592 (ivam
->ivam_release
)(ivam
);
596 * if a port was allocated for this voucher,
597 * it must not have any remaining send rights,
598 * because the port's reference on the voucher
599 * is gone. We can just discard it now.
601 if (IP_VALID(port
)) {
602 require_ip_active(port
);
603 assert(port
->ip_srights
== 0);
605 ipc_port_dealloc_kernel(port
);
609 * the resource manager's control reference and all references
610 * held by the specific value caches are gone, so free the
614 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
615 if (ivac
->ivac_table
[i
].ivace_refs
!= 0) {
616 panic("deallocing a resource manager with live refs to its attr values\n");
620 kfree(ivac
->ivac_table
, ivac
->ivac_table_size
* sizeof(*ivac
->ivac_table
));
621 ivac_lock_destroy(ivac
);
622 zfree(ipc_voucher_attr_control_zone
, ivac
);
626 ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control
)
628 ivac_reference(control
);
632 ipc_voucher_attr_control_release(ipc_voucher_attr_control_t control
)
634 ivac_release(control
);
638 * Routine: convert_port_to_voucher_attr_control reference
640 * Convert from a port to a voucher attribute control.
641 * Doesn't consume the port ref; produces a voucher ref,
646 ipc_voucher_attr_control_t
647 convert_port_to_voucher_attr_control(
650 if (IP_VALID(port
)) {
651 ipc_voucher_attr_control_t ivac
= (ipc_voucher_attr_control_t
) ip_get_kobject(port
);
654 * No need to lock because we have a reference on the
655 * port, and if it is a true voucher control port,
656 * that reference keeps the voucher bound to the port
659 if (ip_kotype(port
) != IKOT_VOUCHER_ATTR_CONTROL
) {
662 require_ip_active(port
);
664 zone_require(ipc_voucher_attr_control_zone
, ivac
);
665 ivac_reference(ivac
);
672 * Routine: ipc_voucher_notify
674 * Called whenever the Mach port system detects no-senders
675 * on the voucher attr control port.
678 ipc_voucher_attr_control_notify(mach_msg_header_t
*msg
)
680 mach_no_senders_notification_t
*notification
= (void *)msg
;
681 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
682 ipc_voucher_attr_control_t ivac
;
684 require_ip_active(port
);
685 assert(IKOT_VOUCHER_ATTR_CONTROL
== ip_kotype(port
));
687 /* release the reference donated by convert_voucher_attr_control_to_port */
688 ivac
= (ipc_voucher_attr_control_t
)ip_get_kobject(port
);
693 * Convert a voucher attr control to a port.
696 convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control
)
698 if (IVAC_NULL
== control
) {
702 zone_require(ipc_voucher_attr_control_zone
, control
);
705 * make a send right and donate our reference for
706 * ipc_voucher_attr_control_notify if this is the first send right
708 if (!ipc_kobject_make_send_lazy_alloc_port(&control
->ivac_port
,
709 (ipc_kobject_t
)control
, IKOT_VOUCHER_ATTR_CONTROL
, false, 0)) {
710 ivac_release(control
);
712 return control
->ivac_port
;
716 * Look up the values for a given <key, index> pair.
720 iv_index_t key_index
,
721 iv_index_t value_index
,
722 mach_voucher_attr_value_handle_array_t values
,
723 mach_voucher_attr_value_handle_array_size_t
*count
)
725 ipc_voucher_attr_control_t ivac
;
728 if (IV_UNUSED_VALINDEX
== value_index
||
729 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
<= key_index
) {
734 ivac
= iv_global_table
[key_index
].ivgte_control
;
735 assert(IVAC_NULL
!= ivac
);
738 * Get the entry and then the linked values.
741 assert(value_index
< ivac
->ivac_table_size
);
742 ivace
= &ivac
->ivac_table
[value_index
];
745 * TODO: support chained values (for effective vouchers).
747 assert(ivace
->ivace_refs
> 0);
748 values
[0] = ivace
->ivace_value
;
754 * ivac_grow_table - Allocate a bigger table of attribute values
756 * Conditions: ivac is locked on entry and again on return
759 ivac_grow_table(ipc_voucher_attr_control_t ivac
)
763 /* NOTE: do not modify *_table and *_size values once set */
764 ivac_entry_t new_table
= NULL
, old_table
= NULL
;
765 iv_index_t new_size
, old_size
;
767 if (ivac
->ivac_is_growing
) {
772 ivac
->ivac_is_growing
= 1;
773 if (ivac
->ivac_table_size
>= IVAC_ENTRIES_MAX
) {
774 panic("Cannot grow ipc space beyond IVAC_ENTRIES_MAX. Some process is leaking vouchers");
778 old_size
= ivac
->ivac_table_size
;
781 new_size
= old_size
* 2;
783 assert(new_size
> old_size
);
784 assert(new_size
< IVAC_ENTRIES_MAX
);
786 new_table
= kalloc(sizeof(ivac_entry
) * new_size
);
788 panic("Failed to grow ivac table to size %d\n", new_size
);
792 /* setup the free list for new entries */
793 for (i
= old_size
; i
< new_size
; i
++) {
794 ivace_reset_data(&new_table
[i
], i
+ 1);
799 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
800 ivace_copy_data(&ivac
->ivac_table
[i
], &new_table
[i
]);
803 old_table
= ivac
->ivac_table
;
805 ivac
->ivac_table
= new_table
;
806 ivac
->ivac_table_size
= new_size
;
808 /* adding new free entries at head of freelist */
809 ivac
->ivac_table
[new_size
- 1].ivace_next
= ivac
->ivac_freelist
;
810 ivac
->ivac_freelist
= old_size
;
811 ivac
->ivac_is_growing
= 0;
816 kfree(old_table
, old_size
* sizeof(ivac_entry
));
822 * ivace_reference_by_index
824 * Take an additional reference on the <key_index, val_index>
825 * cached value. It is assumed the caller already holds a
826 * reference to the same cached key-value pair.
829 ivace_reference_by_index(
830 iv_index_t key_index
,
831 iv_index_t val_index
)
833 ipc_voucher_attr_control_t ivac
;
836 if (IV_UNUSED_VALINDEX
== val_index
) {
840 ivgt_lookup(key_index
, FALSE
, NULL
, &ivac
);
841 assert(IVAC_NULL
!= ivac
);
844 assert(val_index
< ivac
->ivac_table_size
);
845 ivace
= &ivac
->ivac_table
[val_index
];
847 assert(0xdeadc0dedeadc0de != ivace
->ivace_value
);
848 assert(0 < ivace
->ivace_refs
);
849 assert(!ivace
->ivace_free
);
851 /* Take ref only on non-persistent values */
852 if (!ivace
->ivace_persist
) {
860 * Look up the values for a given <key, index> pair.
862 * Consumes a reference on the passed voucher control.
863 * Either it is donated to a newly-created value cache
864 * or it is released (if we piggy back on an existing
865 * value cache entry).
868 ivace_reference_by_value(
869 ipc_voucher_attr_control_t ivac
,
870 mach_voucher_attr_value_handle_t value
,
871 mach_voucher_attr_value_flags_t flag
)
873 ivac_entry_t ivace
= IVACE_NULL
;
874 iv_index_t hash_index
;
877 if (IVAC_NULL
== ivac
) {
878 return IV_UNUSED_VALINDEX
;
883 hash_index
= IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
884 index
= ivac
->ivac_table
[hash_index
].ivace_index
;
885 while (index
!= IV_HASH_END
) {
886 assert(index
< ivac
->ivac_table_size
);
887 ivace
= &ivac
->ivac_table
[index
];
888 assert(!ivace
->ivace_free
);
890 if (ivace
->ivace_value
== value
) {
894 assert(ivace
->ivace_next
!= index
);
895 index
= ivace
->ivace_next
;
899 if (index
!= IV_HASH_END
) {
900 /* only add reference on non-persistent value */
901 if (!ivace
->ivace_persist
) {
911 /* insert new entry in the table */
912 index
= ivac
->ivac_freelist
;
913 if (IV_FREELIST_END
== index
) {
915 ivac_grow_table(ivac
);
919 /* take the entry off the freelist */
920 ivace
= &ivac
->ivac_table
[index
];
921 ivac
->ivac_freelist
= ivace
->ivace_next
;
923 /* initialize the new entry */
924 ivace
->ivace_value
= value
;
925 ivace
->ivace_refs
= 1;
926 ivace
->ivace_made
= 1;
927 ivace
->ivace_free
= FALSE
;
928 ivace
->ivace_persist
= (flag
& MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
) ? TRUE
: FALSE
;
930 /* insert the new entry in the proper hash chain */
931 ivace
->ivace_next
= ivac
->ivac_table
[hash_index
].ivace_index
;
932 ivac
->ivac_table
[hash_index
].ivace_index
= index
;
935 /* donated passed in ivac reference to new entry */
941 * Release a reference on the given <key_index, value_index> pair.
943 * Conditions: called with nothing locked, as it may cause
944 * callouts and/or messaging to the resource
949 iv_index_t key_index
,
950 iv_index_t value_index
)
952 ipc_voucher_attr_control_t ivac
;
953 ipc_voucher_attr_manager_t ivam
;
954 mach_voucher_attr_value_handle_t value
;
955 mach_voucher_attr_value_reference_t made
;
956 mach_voucher_attr_key_t key
;
957 iv_index_t hash_index
;
961 /* cant release the default value */
962 if (IV_UNUSED_VALINDEX
== value_index
) {
966 ivgt_lookup(key_index
, FALSE
, &ivam
, &ivac
);
967 assert(IVAC_NULL
!= ivac
);
968 assert(IVAM_NULL
!= ivam
);
971 assert(value_index
< ivac
->ivac_table_size
);
972 ivace
= &ivac
->ivac_table
[value_index
];
974 assert(0 < ivace
->ivace_refs
);
976 /* cant release persistent values */
977 if (ivace
->ivace_persist
) {
982 if (0 < --ivace
->ivace_refs
) {
987 key
= iv_index_to_key(key_index
);
988 assert(MACH_VOUCHER_ATTR_KEY_NONE
!= key
);
991 * if last return reply is still pending,
992 * let it handle this later return when
993 * the previous reply comes in.
995 if (ivace
->ivace_releasing
) {
1000 /* claim releasing */
1001 ivace
->ivace_releasing
= TRUE
;
1002 value
= ivace
->ivace_value
;
1005 assert(value
== ivace
->ivace_value
);
1006 assert(!ivace
->ivace_free
);
1007 made
= ivace
->ivace_made
;
1010 /* callout to manager's release_value */
1011 kr
= (ivam
->ivam_release_value
)(ivam
, key
, value
, made
);
1013 /* recalculate entry address as table may have changed */
1015 ivace
= &ivac
->ivac_table
[value_index
];
1016 assert(value
== ivace
->ivace_value
);
1019 * new made values raced with this return. If the
1020 * manager OK'ed the prior release, we have to start
1021 * the made numbering over again (pretend the race
1022 * didn't happen). If the entry has zero refs again,
1023 * re-drive the release.
1025 if (ivace
->ivace_made
!= made
) {
1026 if (KERN_SUCCESS
== kr
) {
1027 ivace
->ivace_made
-= made
;
1030 if (0 == ivace
->ivace_refs
) {
1034 ivace
->ivace_releasing
= FALSE
;
1039 * If the manager returned FAILURE, someone took a
1040 * reference on the value but have not updated the ivace,
1041 * release the lock and return since thread who got
1042 * the new reference will update the ivace and will have
1043 * non-zero reference on the value.
1045 if (KERN_SUCCESS
!= kr
) {
1046 ivace
->ivace_releasing
= FALSE
;
1052 assert(0 == ivace
->ivace_refs
);
1055 * going away - remove entry from its hash
1056 * If its at the head of the hash bucket list (common), unchain
1057 * at the head. Otherwise walk the chain until the next points
1058 * at this entry, and remove it from the the list there.
1060 hash_index
= iv_hash_value(key_index
, value
);
1061 if (ivac
->ivac_table
[hash_index
].ivace_index
== value_index
) {
1062 ivac
->ivac_table
[hash_index
].ivace_index
= ivace
->ivace_next
;
1064 hash_index
= ivac
->ivac_table
[hash_index
].ivace_index
;
1065 assert(IV_HASH_END
!= hash_index
);
1066 while (ivac
->ivac_table
[hash_index
].ivace_next
!= value_index
) {
1067 hash_index
= ivac
->ivac_table
[hash_index
].ivace_next
;
1068 assert(IV_HASH_END
!= hash_index
);
1070 ivac
->ivac_table
[hash_index
].ivace_next
= ivace
->ivace_next
;
1073 /* Put this entry on the freelist */
1074 ivace
->ivace_value
= 0xdeadc0dedeadc0de;
1075 ivace
->ivace_releasing
= FALSE
;
1076 ivace
->ivace_free
= TRUE
;
1077 ivace
->ivace_made
= 0;
1078 ivace
->ivace_next
= ivac
->ivac_freelist
;
1079 ivac
->ivac_freelist
= value_index
;
1082 /* release the reference this value held on its cache control */
1092 * Lookup an entry in the global table from the context of a manager
1093 * registration. Adds a reference to the control to keep the results
1094 * around (if needed).
1096 * Because of the calling point, we can't be sure the manager is
1097 * [fully] registered yet. So, we must hold the global table lock
1098 * during the lookup to synchronize with in-parallel registrations
1099 * (and possible table growth).
1102 ivgt_lookup(iv_index_t key_index
,
1103 boolean_t take_reference
,
1104 ipc_voucher_attr_manager_t
*manager
,
1105 ipc_voucher_attr_control_t
*control
)
1107 ipc_voucher_attr_control_t ivac
;
1109 if (key_index
< MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
) {
1111 if (NULL
!= manager
) {
1112 *manager
= iv_global_table
[key_index
].ivgte_manager
;
1114 ivac
= iv_global_table
[key_index
].ivgte_control
;
1115 if (IVAC_NULL
!= ivac
) {
1116 assert(key_index
== ivac
->ivac_key_index
);
1117 if (take_reference
) {
1118 assert(NULL
!= control
);
1119 ivac_reference(ivac
);
1123 if (NULL
!= control
) {
1127 if (NULL
!= manager
) {
1128 *manager
= IVAM_NULL
;
1130 if (NULL
!= control
) {
1131 *control
= IVAC_NULL
;
1137 * Routine: ipc_replace_voucher_value
1139 * Replace the <voucher, key> value with the results of
1140 * running the supplied command through the resource
1141 * manager's get-value callback.
1143 * Nothing locked (may invoke user-space repeatedly).
1144 * Caller holds references on voucher and previous voucher.
1146 static kern_return_t
1147 ipc_replace_voucher_value(
1148 ipc_voucher_t voucher
,
1149 mach_voucher_attr_key_t key
,
1150 mach_voucher_attr_recipe_command_t command
,
1151 ipc_voucher_t prev_voucher
,
1152 mach_voucher_attr_content_t content
,
1153 mach_voucher_attr_content_size_t content_size
)
1155 mach_voucher_attr_value_handle_t previous_vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1156 mach_voucher_attr_value_handle_array_size_t previous_vals_count
;
1157 mach_voucher_attr_value_handle_t new_value
;
1158 mach_voucher_attr_value_flags_t new_flag
;
1159 ipc_voucher_t new_value_voucher
;
1160 ipc_voucher_attr_manager_t ivam
;
1161 ipc_voucher_attr_control_t ivac
;
1162 iv_index_t prev_val_index
;
1163 iv_index_t save_val_index
;
1164 iv_index_t val_index
;
1165 iv_index_t key_index
;
1169 * Get the manager for this key_index.
1170 * Returns a reference on the control.
1172 key_index
= iv_key_to_index(key
);
1173 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1174 if (IVAM_NULL
== ivam
) {
1175 return KERN_INVALID_ARGUMENT
;
1178 /* save the current value stored in the forming voucher */
1179 save_val_index
= iv_lookup(voucher
, key_index
);
1182 * Get the previous value(s) for this key creation.
1183 * If a previous voucher is specified, they come from there.
1184 * Otherwise, they come from the intermediate values already
1185 * in the forming voucher.
1187 prev_val_index
= (IV_NULL
!= prev_voucher
) ?
1188 iv_lookup(prev_voucher
, key_index
) :
1190 ivace_lookup_values(key_index
, prev_val_index
,
1191 previous_vals
, &previous_vals_count
);
1193 /* Call out to resource manager to get new value */
1194 new_value_voucher
= IV_NULL
;
1195 kr
= (ivam
->ivam_get_value
)(
1197 previous_vals
, previous_vals_count
,
1198 content
, content_size
,
1199 &new_value
, &new_flag
, &new_value_voucher
);
1200 if (KERN_SUCCESS
!= kr
) {
1205 /* TODO: value insertion from returned voucher */
1206 if (IV_NULL
!= new_value_voucher
) {
1207 iv_release(new_value_voucher
);
1211 * Find or create a slot in the table associated
1212 * with this attribute value. The ivac reference
1213 * is transferred to a new value, or consumed if
1214 * we find a matching existing value.
1216 val_index
= ivace_reference_by_value(ivac
, new_value
, new_flag
);
1217 iv_set(voucher
, key_index
, val_index
);
1220 * release saved old value from the newly forming voucher
1221 * This is saved until the end to avoid churning the
1222 * release logic in cases where the same value is returned
1223 * as was there before.
1225 ivace_release(key_index
, save_val_index
);
1227 return KERN_SUCCESS
;
1231 * Routine: ipc_directly_replace_voucher_value
1233 * Replace the <voucher, key> value with the value-handle
1234 * supplied directly by the attribute manager.
1237 * Caller holds references on voucher.
1238 * A made reference to the value-handle is donated by the caller.
1240 static kern_return_t
1241 ipc_directly_replace_voucher_value(
1242 ipc_voucher_t voucher
,
1243 mach_voucher_attr_key_t key
,
1244 mach_voucher_attr_value_handle_t new_value
)
1246 ipc_voucher_attr_manager_t ivam
;
1247 ipc_voucher_attr_control_t ivac
;
1248 iv_index_t save_val_index
;
1249 iv_index_t val_index
;
1250 iv_index_t key_index
;
1253 * Get the manager for this key_index.
1254 * Returns a reference on the control.
1256 key_index
= iv_key_to_index(key
);
1257 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1258 if (IVAM_NULL
== ivam
) {
1259 return KERN_INVALID_ARGUMENT
;
1262 /* save the current value stored in the forming voucher */
1263 save_val_index
= iv_lookup(voucher
, key_index
);
1266 * Find or create a slot in the table associated
1267 * with this attribute value. The ivac reference
1268 * is transferred to a new value, or consumed if
1269 * we find a matching existing value.
1271 val_index
= ivace_reference_by_value(ivac
, new_value
,
1272 MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
);
1273 iv_set(voucher
, key_index
, val_index
);
1276 * release saved old value from the newly forming voucher
1277 * This is saved until the end to avoid churning the
1278 * release logic in cases where the same value is returned
1279 * as was there before.
1281 ivace_release(key_index
, save_val_index
);
1283 return KERN_SUCCESS
;
1286 static kern_return_t
1287 ipc_execute_voucher_recipe_command(
1288 ipc_voucher_t voucher
,
1289 mach_voucher_attr_key_t key
,
1290 mach_voucher_attr_recipe_command_t command
,
1291 ipc_voucher_t prev_iv
,
1292 mach_voucher_attr_content_t content
,
1293 mach_voucher_attr_content_size_t content_size
,
1296 iv_index_t prev_val_index
;
1297 iv_index_t val_index
;
1302 * MACH_VOUCHER_ATTR_COPY
1303 * Copy the attribute(s) from the previous voucher to the new
1304 * one. A wildcard key is an acceptable value - indicating a
1305 * desire to copy all the attribute values from the previous
1308 case MACH_VOUCHER_ATTR_COPY
:
1310 /* no recipe data on a copy */
1311 if (0 < content_size
) {
1312 return KERN_INVALID_ARGUMENT
;
1315 /* nothing to copy from? - done */
1316 if (IV_NULL
== prev_iv
) {
1317 return KERN_SUCCESS
;
1320 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1321 iv_index_t limit
, j
;
1323 /* reconcile possible difference in voucher sizes */
1324 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1325 prev_iv
->iv_table_size
:
1326 voucher
->iv_table_size
;
1328 /* wildcard matching */
1329 for (j
= 0; j
< limit
; j
++) {
1330 /* release old value being replaced */
1331 val_index
= iv_lookup(voucher
, j
);
1332 ivace_release(j
, val_index
);
1334 /* replace with reference to prev voucher's value */
1335 prev_val_index
= iv_lookup(prev_iv
, j
);
1336 ivace_reference_by_index(j
, prev_val_index
);
1337 iv_set(voucher
, j
, prev_val_index
);
1340 iv_index_t key_index
;
1342 /* copy just one key */
1343 key_index
= iv_key_to_index(key
);
1344 if (ivgt_keys_in_use
< key_index
) {
1345 return KERN_INVALID_ARGUMENT
;
1348 /* release old value being replaced */
1349 val_index
= iv_lookup(voucher
, key_index
);
1350 ivace_release(key_index
, val_index
);
1352 /* replace with reference to prev voucher's value */
1353 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1354 ivace_reference_by_index(key_index
, prev_val_index
);
1355 iv_set(voucher
, key_index
, prev_val_index
);
1360 * MACH_VOUCHER_ATTR_REMOVE
1361 * Remove the attribute(s) from the under construction voucher.
1362 * A wildcard key is an acceptable value - indicating a desire
1363 * to remove all the attribute values set up so far in the voucher.
1364 * If a previous voucher is specified, only remove the value it
1365 * it matches the value in the previous voucher.
1367 case MACH_VOUCHER_ATTR_REMOVE
:
1368 /* no recipe data on a remove */
1369 if (0 < content_size
) {
1370 return KERN_INVALID_ARGUMENT
;
1373 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1374 iv_index_t limit
, j
;
1376 /* reconcile possible difference in voucher sizes */
1377 limit
= (IV_NULL
== prev_iv
) ? voucher
->iv_table_size
:
1378 ((prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1379 prev_iv
->iv_table_size
: voucher
->iv_table_size
);
1381 /* wildcard matching */
1382 for (j
= 0; j
< limit
; j
++) {
1383 val_index
= iv_lookup(voucher
, j
);
1385 /* If not matched in previous, skip */
1386 if (IV_NULL
!= prev_iv
) {
1387 prev_val_index
= iv_lookup(prev_iv
, j
);
1388 if (val_index
!= prev_val_index
) {
1392 /* release and clear */
1393 ivace_release(j
, val_index
);
1394 iv_set(voucher
, j
, IV_UNUSED_VALINDEX
);
1397 iv_index_t key_index
;
1399 /* copy just one key */
1400 key_index
= iv_key_to_index(key
);
1401 if (ivgt_keys_in_use
< key_index
) {
1402 return KERN_INVALID_ARGUMENT
;
1405 val_index
= iv_lookup(voucher
, key_index
);
1407 /* If not matched in previous, skip */
1408 if (IV_NULL
!= prev_iv
) {
1409 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1410 if (val_index
!= prev_val_index
) {
1415 /* release and clear */
1416 ivace_release(key_index
, val_index
);
1417 iv_set(voucher
, key_index
, IV_UNUSED_VALINDEX
);
1422 * MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
1423 * Use key-privilege to set a value handle for the attribute directly,
1424 * rather than triggering a callback into the attribute manager to
1425 * interpret a recipe to generate the value handle.
1427 case MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
:
1429 mach_voucher_attr_value_handle_t new_value
;
1431 if (sizeof(mach_voucher_attr_value_handle_t
) != content_size
) {
1432 return KERN_INVALID_ARGUMENT
;
1435 new_value
= *(mach_voucher_attr_value_handle_t
*)(void *)content
;
1436 kr
= ipc_directly_replace_voucher_value(voucher
,
1438 if (KERN_SUCCESS
!= kr
) {
1442 return KERN_INVALID_CAPABILITY
;
1447 * MACH_VOUCHER_ATTR_REDEEM
1448 * Redeem the attribute(s) from the previous voucher for a possibly
1449 * new value in the new voucher. A wildcard key is an acceptable value,
1450 * indicating a desire to redeem all the values.
1452 case MACH_VOUCHER_ATTR_REDEEM
:
1454 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1455 iv_index_t limit
, j
;
1457 /* reconcile possible difference in voucher sizes */
1458 if (IV_NULL
!= prev_iv
) {
1459 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1460 prev_iv
->iv_table_size
:
1461 voucher
->iv_table_size
;
1463 limit
= voucher
->iv_table_size
;
1466 /* wildcard matching */
1467 for (j
= 0; j
< limit
; j
++) {
1468 mach_voucher_attr_key_t j_key
;
1470 j_key
= iv_index_to_key(j
);
1472 /* skip non-existent managers */
1473 if (MACH_VOUCHER_ATTR_KEY_NONE
== j_key
) {
1477 /* get the new value from redeem (skip empty previous) */
1478 kr
= ipc_replace_voucher_value(voucher
,
1484 if (KERN_SUCCESS
!= kr
) {
1490 OS_FALLTHROUGH
; /* fall thru for single key redemption */
1494 * Replace the current value for the <voucher, key> pair with whatever
1495 * value the resource manager returns for the command and recipe
1496 * combination provided.
1499 kr
= ipc_replace_voucher_value(voucher
,
1505 if (KERN_SUCCESS
!= kr
) {
1511 return KERN_SUCCESS
;
1515 * Routine: iv_checksum
1517 * Compute the voucher sum. This is more position-
1518 * relevant than many other checksums - important for
1519 * vouchers (arrays of low, oft-reused, indexes).
1521 static inline iv_index_t
1522 iv_checksum(ipc_voucher_t voucher
, boolean_t
*emptyp
)
1526 boolean_t empty
= TRUE
;
1527 if (0 < voucher
->iv_table_size
) {
1528 iv_index_t i
= voucher
->iv_table_size
- 1;
1531 iv_index_t v
= voucher
->iv_table
[i
];
1532 c
= c
<< 3 | c
>> (32 - 3); /* rotate */
1533 c
= ~c
; /* invert */
1535 c
+= v
; /* add in */
1547 * See if the set of values represented by this new voucher
1548 * already exist in another voucher. If so return a reference
1549 * to the existing voucher and deallocate the voucher provided.
1550 * Otherwise, insert this one in the hash and return it.
1552 * A voucher reference is donated on entry.
1554 * A voucher reference (may be different than on entry).
1556 static ipc_voucher_t
1557 iv_dedup(ipc_voucher_t new_iv
)
1564 sum
= iv_checksum(new_iv
, &empty
);
1566 /* If all values are default, that's the empty (NULL) voucher */
1568 iv_dealloc(new_iv
, FALSE
);
1572 hash
= IV_HASH_BUCKET(sum
);
1575 queue_iterate(&ivht_bucket
[hash
], iv
, ipc_voucher_t
, iv_hash_link
) {
1576 assert(iv
->iv_hash
== hash
);
1578 /* if not already deallocating and sums match... */
1579 if ((os_ref_get_count(&iv
->iv_refs
) > 0) && (iv
->iv_sum
== sum
)) {
1582 assert(iv
->iv_table_size
<= new_iv
->iv_table_size
);
1584 /* and common entries match... */
1585 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
1586 if (iv
->iv_table
[i
] != new_iv
->iv_table
[i
]) {
1590 if (i
< iv
->iv_table_size
) {
1594 /* and all extra entries in new one are unused... */
1595 while (i
< new_iv
->iv_table_size
) {
1596 if (new_iv
->iv_table
[i
++] != IV_UNUSED_VALINDEX
) {
1600 if (i
< new_iv
->iv_table_size
) {
1604 /* ... we found a match... */
1606 /* can we get a ref before it hits 0
1608 * This is thread safe. If the reference count is zero before we
1609 * adjust it, no other thread can have a reference to the voucher.
1610 * The dealloc code requires holding the ivht_lock, so
1611 * the voucher cannot be yanked out from under us.
1613 if (!os_ref_retain_try(&iv
->iv_refs
)) {
1619 /* referenced previous, so deallocate the new one */
1620 iv_dealloc(new_iv
, FALSE
);
1625 /* add the new voucher to the hash, and return it */
1626 new_iv
->iv_sum
= sum
;
1627 new_iv
->iv_hash
= hash
;
1628 queue_enter(&ivht_bucket
[hash
], new_iv
, ipc_voucher_t
, iv_hash_link
);
1633 * This code is disabled for KDEBUG_LEVEL_IST and KDEBUG_LEVEL_NONE
1635 #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD)
1636 if (kdebug_enable
& ~KDEBUG_ENABLE_PPT
) {
1637 uintptr_t voucher_addr
= VM_KERNEL_ADDRPERM((uintptr_t)new_iv
);
1638 uintptr_t attr_tracepoints_needed
= 0;
1640 if (ipc_voucher_trace_contents
) {
1642 * voucher_contents sizing is a bit more constrained
1643 * than might be obvious.
1645 * This is typically a uint8_t typed array. However,
1646 * we want to access it as a uintptr_t to efficiently
1647 * copyout the data in tracepoints.
1649 * This constrains the size to uintptr_t bytes, and
1650 * adds a minimimum alignment requirement equivalent
1653 * Further constraining the size is the fact that it
1654 * is copied out 4 uintptr_t chunks at a time. We do
1655 * NOT want to run off the end of the array and copyout
1656 * random stack data.
1658 * So the minimum size is 4 * sizeof(uintptr_t), and
1659 * the minimum alignment is uintptr_t aligned.
1662 #define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
1663 #define PAYLOAD_SIZE 1024
1665 static_assert(PAYLOAD_SIZE
% PAYLOAD_PER_TRACEPOINT
== 0, "size invariant violated");
1667 mach_voucher_attr_raw_recipe_array_size_t payload_size
= PAYLOAD_SIZE
;
1668 uintptr_t payload
[PAYLOAD_SIZE
/ sizeof(uintptr_t)];
1671 kr
= mach_voucher_extract_all_attr_recipes(new_iv
, (mach_voucher_attr_raw_recipe_array_t
)payload
, &payload_size
);
1672 if (KERN_SUCCESS
== kr
) {
1673 attr_tracepoints_needed
= (payload_size
+ PAYLOAD_PER_TRACEPOINT
- 1) / PAYLOAD_PER_TRACEPOINT
;
1676 * To prevent leaking data from the stack, we
1677 * need to zero data to the end of a tracepoint
1680 size_t remainder
= payload_size
% PAYLOAD_PER_TRACEPOINT
;
1682 bzero((uint8_t*)payload
+ payload_size
,
1683 PAYLOAD_PER_TRACEPOINT
- remainder
);
1687 KDBG(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_CREATE
),
1688 voucher_addr
, new_iv
->iv_table_size
, ivht_count
,
1691 uintptr_t index
= 0;
1692 while (attr_tracepoints_needed
--) {
1693 KDBG(MACHDBG_CODE(DBG_MACH_IPC
,
1694 MACH_IPC_VOUCHER_CREATE_ATTR_DATA
), payload
[index
],
1695 payload
[index
+ 1], payload
[index
+ 2],
1696 payload
[index
+ 3]);
1700 KDBG(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_CREATE
),
1701 voucher_addr
, new_iv
->iv_table_size
, ivht_count
);
1704 #endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
1710 * Routine: ipc_create_mach_voucher
1712 * Create a new mach voucher and initialize it with the
1713 * value(s) created by having the appropriate resource
1714 * managers interpret the supplied recipe commands and
1717 * Nothing locked (may invoke user-space repeatedly).
1718 * Caller holds references on previous vouchers.
1719 * Previous vouchers are passed as voucher indexes.
1722 ipc_create_mach_voucher(
1723 ipc_voucher_attr_raw_recipe_array_t recipes
,
1724 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1725 ipc_voucher_t
*new_voucher
)
1727 ipc_voucher_attr_recipe_t sub_recipe
;
1728 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1729 ipc_voucher_t voucher
;
1730 kern_return_t kr
= KERN_SUCCESS
;
1732 /* if nothing to do ... */
1733 if (0 == recipe_size
) {
1734 *new_voucher
= IV_NULL
;
1735 return KERN_SUCCESS
;
1738 /* allocate a voucher */
1739 voucher
= iv_alloc(ivgt_keys_in_use
);
1740 if (IV_NULL
== voucher
) {
1741 return KERN_RESOURCE_SHORTAGE
;
1744 /* iterate over the recipe items */
1745 while (0 < recipe_size
- recipe_used
) {
1746 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1747 kr
= KERN_INVALID_ARGUMENT
;
1751 /* find the next recipe */
1752 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1753 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1754 kr
= KERN_INVALID_ARGUMENT
;
1757 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1759 kr
= ipc_execute_voucher_recipe_command(voucher
,
1761 sub_recipe
->command
,
1762 sub_recipe
->previous_voucher
,
1763 sub_recipe
->content
,
1764 sub_recipe
->content_size
,
1766 if (KERN_SUCCESS
!= kr
) {
1771 if (KERN_SUCCESS
== kr
) {
1772 *new_voucher
= iv_dedup(voucher
);
1774 iv_dealloc(voucher
, FALSE
);
1775 *new_voucher
= IV_NULL
;
1781 * Routine: ipc_voucher_attr_control_create_mach_voucher
1783 * Create a new mach voucher and initialize it with the
1784 * value(s) created by having the appropriate resource
1785 * managers interpret the supplied recipe commands and
1788 * The resource manager control's privilege over its
1789 * particular key value is reflected on to the execution
1790 * code, allowing internal commands (like setting a
1791 * key value handle directly, rather than having to
1792 * create a recipe, that will generate a callback just
1796 * Nothing locked (may invoke user-space repeatedly).
1797 * Caller holds references on previous vouchers.
1798 * Previous vouchers are passed as voucher indexes.
1801 ipc_voucher_attr_control_create_mach_voucher(
1802 ipc_voucher_attr_control_t control
,
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 mach_voucher_attr_key_t control_key
;
1808 ipc_voucher_attr_recipe_t sub_recipe
;
1809 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1810 ipc_voucher_t voucher
= IV_NULL
;
1811 kern_return_t kr
= KERN_SUCCESS
;
1813 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
1814 return KERN_INVALID_CAPABILITY
;
1817 /* if nothing to do ... */
1818 if (0 == recipe_size
) {
1819 *new_voucher
= IV_NULL
;
1820 return KERN_SUCCESS
;
1823 /* allocate new voucher */
1824 voucher
= iv_alloc(ivgt_keys_in_use
);
1825 if (IV_NULL
== voucher
) {
1826 return KERN_RESOURCE_SHORTAGE
;
1829 control_key
= iv_index_to_key(control
->ivac_key_index
);
1831 /* iterate over the recipe items */
1832 while (0 < recipe_size
- recipe_used
) {
1833 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1834 kr
= KERN_INVALID_ARGUMENT
;
1838 /* find the next recipe */
1839 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1840 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1841 kr
= KERN_INVALID_ARGUMENT
;
1844 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1846 kr
= ipc_execute_voucher_recipe_command(voucher
,
1848 sub_recipe
->command
,
1849 sub_recipe
->previous_voucher
,
1850 sub_recipe
->content
,
1851 sub_recipe
->content_size
,
1852 (sub_recipe
->key
== control_key
));
1853 if (KERN_SUCCESS
!= kr
) {
1858 if (KERN_SUCCESS
== kr
) {
1859 *new_voucher
= iv_dedup(voucher
);
1861 *new_voucher
= IV_NULL
;
1862 iv_dealloc(voucher
, FALSE
);
1868 * ipc_register_well_known_mach_voucher_attr_manager
1870 * Register the resource manager responsible for a given key value.
1873 ipc_register_well_known_mach_voucher_attr_manager(
1874 ipc_voucher_attr_manager_t manager
,
1875 mach_voucher_attr_value_handle_t default_value
,
1876 mach_voucher_attr_key_t key
,
1877 ipc_voucher_attr_control_t
*control
)
1879 ipc_voucher_attr_control_t new_control
;
1880 iv_index_t key_index
;
1881 iv_index_t hash_index
;
1883 if (IVAM_NULL
== manager
) {
1884 return KERN_INVALID_ARGUMENT
;
1887 key_index
= iv_key_to_index(key
);
1888 if (IV_UNUSED_KEYINDEX
== key_index
) {
1889 return KERN_INVALID_ARGUMENT
;
1892 new_control
= ivac_alloc(key_index
);
1893 if (IVAC_NULL
== new_control
) {
1894 return KERN_RESOURCE_SHORTAGE
;
1897 /* insert the default value into slot 0 */
1898 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_value
= default_value
;
1899 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_refs
= IVACE_REFS_MAX
;
1900 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_made
= IVACE_REFS_MAX
;
1901 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_persist
= TRUE
;
1902 assert(IV_HASH_END
== new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_next
);
1905 if (IVAM_NULL
!= iv_global_table
[key_index
].ivgte_manager
) {
1907 ivac_release(new_control
);
1908 return KERN_INVALID_ARGUMENT
;
1911 /* fill in the global table slot for this key */
1912 iv_global_table
[key_index
].ivgte_manager
= manager
;
1913 iv_global_table
[key_index
].ivgte_control
= new_control
;
1914 iv_global_table
[key_index
].ivgte_key
= key
;
1916 /* insert the default value into the hash (in case it is returned later) */
1917 hash_index
= iv_hash_value(key_index
, default_value
);
1918 assert(IV_HASH_END
== new_control
->ivac_table
[hash_index
].ivace_index
);
1919 new_control
->ivac_table
[hash_index
].ivace_index
= IV_UNUSED_VALINDEX
;
1923 /* return the reference on the new cache control to the caller */
1924 *control
= new_control
;
1926 return KERN_SUCCESS
;
1930 * Routine: mach_voucher_extract_attr_content
1932 * Extract the content for a given <voucher, key> pair.
1934 * If a value other than the default is present for this
1935 * <voucher,key> pair, we need to contact the resource
1936 * manager to extract the content/meaning of the value(s)
1937 * present. Otherwise, return success (but no data).
1940 * Nothing locked - as it may upcall to user-space.
1941 * The caller holds a reference on the voucher.
1944 mach_voucher_extract_attr_content(
1945 ipc_voucher_t voucher
,
1946 mach_voucher_attr_key_t key
,
1947 mach_voucher_attr_content_t content
,
1948 mach_voucher_attr_content_size_t
*in_out_size
)
1950 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1951 mach_voucher_attr_value_handle_array_size_t vals_count
;
1952 mach_voucher_attr_recipe_command_t command
;
1953 ipc_voucher_attr_manager_t manager
;
1954 iv_index_t value_index
;
1955 iv_index_t key_index
;
1959 if (IV_NULL
== voucher
) {
1960 return KERN_INVALID_ARGUMENT
;
1963 key_index
= iv_key_to_index(key
);
1965 value_index
= iv_lookup(voucher
, key_index
);
1966 if (IV_UNUSED_VALINDEX
== value_index
) {
1968 return KERN_SUCCESS
;
1972 * Get the manager for this key_index. The
1973 * existence of a non-default value for this
1974 * slot within our voucher will keep the
1975 * manager referenced during the callout.
1977 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
1978 if (IVAM_NULL
== manager
) {
1979 return KERN_INVALID_ARGUMENT
;
1983 * Get the value(s) to pass to the manager
1984 * for this value_index.
1986 ivace_lookup_values(key_index
, value_index
,
1988 assert(0 < vals_count
);
1990 /* callout to manager */
1992 kr
= (manager
->ivam_extract_content
)(manager
, key
,
1993 vals
, vals_count
, &command
, content
, in_out_size
);
1998 * Routine: mach_voucher_extract_attr_recipe
2000 * Extract a recipe for a given <voucher, key> pair.
2002 * If a value other than the default is present for this
2003 * <voucher,key> pair, we need to contact the resource
2004 * manager to extract the content/meaning of the value(s)
2005 * present. Otherwise, return success (but no data).
2008 * Nothing locked - as it may upcall to user-space.
2009 * The caller holds a reference on the voucher.
2012 mach_voucher_extract_attr_recipe(
2013 ipc_voucher_t voucher
,
2014 mach_voucher_attr_key_t key
,
2015 mach_voucher_attr_raw_recipe_t raw_recipe
,
2016 mach_voucher_attr_raw_recipe_size_t
*in_out_size
)
2018 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2019 mach_voucher_attr_value_handle_array_size_t vals_count
;
2020 ipc_voucher_attr_manager_t manager
;
2021 mach_voucher_attr_recipe_t recipe
;
2022 iv_index_t value_index
;
2023 iv_index_t key_index
;
2027 if (IV_NULL
== voucher
) {
2028 return KERN_INVALID_ARGUMENT
;
2031 key_index
= iv_key_to_index(key
);
2033 value_index
= iv_lookup(voucher
, key_index
);
2034 if (IV_UNUSED_VALINDEX
== value_index
) {
2036 return KERN_SUCCESS
;
2039 if (*in_out_size
< sizeof(*recipe
)) {
2040 return KERN_NO_SPACE
;
2043 recipe
= (mach_voucher_attr_recipe_t
)(void *)raw_recipe
;
2045 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2046 recipe
->previous_voucher
= MACH_VOUCHER_NAME_NULL
;
2047 recipe
->content_size
= *in_out_size
- sizeof(*recipe
);
2050 * Get the manager for this key_index. The
2051 * existence of a non-default value for this
2052 * slot within our voucher will keep the
2053 * manager referenced during the callout.
2055 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2056 if (IVAM_NULL
== manager
) {
2057 return KERN_INVALID_ARGUMENT
;
2061 * Get the value(s) to pass to the manager
2062 * for this value_index.
2064 ivace_lookup_values(key_index
, value_index
,
2066 assert(0 < vals_count
);
2068 /* callout to manager */
2069 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2072 recipe
->content
, &recipe
->content_size
);
2073 if (KERN_SUCCESS
== kr
) {
2074 assert(*in_out_size
- sizeof(*recipe
) >= recipe
->content_size
);
2075 *in_out_size
= sizeof(*recipe
) + recipe
->content_size
;
2084 * Routine: mach_voucher_extract_all_attr_recipes
2086 * Extract all the (non-default) contents for a given voucher,
2087 * building up a recipe that could be provided to a future
2088 * voucher creation call.
2090 * Nothing locked (may invoke user-space).
2091 * Caller holds a reference on the supplied voucher.
2094 mach_voucher_extract_all_attr_recipes(
2095 ipc_voucher_t voucher
,
2096 mach_voucher_attr_raw_recipe_array_t recipes
,
2097 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2099 mach_voucher_attr_recipe_size_t recipe_size
= *in_out_size
;
2100 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2101 iv_index_t key_index
;
2103 if (IV_NULL
== voucher
) {
2104 return KERN_INVALID_ARGUMENT
;
2107 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2108 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2109 mach_voucher_attr_value_handle_array_size_t vals_count
;
2110 mach_voucher_attr_content_size_t content_size
;
2111 ipc_voucher_attr_manager_t manager
;
2112 mach_voucher_attr_recipe_t recipe
;
2113 mach_voucher_attr_key_t key
;
2114 iv_index_t value_index
;
2117 /* don't output anything for a default value */
2118 value_index
= iv_lookup(voucher
, key_index
);
2119 if (IV_UNUSED_VALINDEX
== value_index
) {
2123 if (recipe_size
- recipe_used
< sizeof(*recipe
)) {
2124 return KERN_NO_SPACE
;
2128 * Get the manager for this key_index. The
2129 * existence of a non-default value for this
2130 * slot within our voucher will keep the
2131 * manager referenced during the callout.
2133 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2134 assert(IVAM_NULL
!= manager
);
2135 if (IVAM_NULL
== manager
) {
2139 recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2140 content_size
= recipe_size
- recipe_used
- sizeof(*recipe
);
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 key
= iv_index_to_key(key_index
);
2153 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2154 recipe
->content_size
= content_size
;
2156 /* callout to manager */
2157 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2160 recipe
->content
, &recipe
->content_size
);
2161 if (KERN_SUCCESS
!= kr
) {
2165 assert(recipe
->content_size
<= content_size
);
2166 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2169 *in_out_size
= recipe_used
;
2170 return KERN_SUCCESS
;
2174 * Routine: mach_voucher_debug_info
2176 * Extract all the (non-default) contents for a given mach port name,
2177 * building up a recipe that could be provided to a future
2178 * voucher creation call.
2180 * Nothing locked (may invoke user-space).
2181 * Caller may not hold a reference on the supplied voucher.
2183 #if !(DEVELOPMENT || DEBUG)
2185 mach_voucher_debug_info(
2186 ipc_space_t __unused space
,
2187 mach_port_name_t __unused voucher_name
,
2188 mach_voucher_attr_raw_recipe_array_t __unused recipes
,
2189 mach_voucher_attr_raw_recipe_array_size_t __unused
*in_out_size
)
2191 return KERN_NOT_SUPPORTED
;
2195 mach_voucher_debug_info(
2197 mach_port_name_t voucher_name
,
2198 mach_voucher_attr_raw_recipe_array_t recipes
,
2199 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2201 ipc_voucher_t voucher
= IPC_VOUCHER_NULL
;
2203 ipc_port_t port
= MACH_PORT_NULL
;
2205 if (space
== IS_NULL
) {
2206 return KERN_INVALID_TASK
;
2209 if (!MACH_PORT_VALID(voucher_name
)) {
2210 return KERN_INVALID_ARGUMENT
;
2213 kr
= ipc_port_translate_send(space
, voucher_name
, &port
);
2214 if (KERN_SUCCESS
!= kr
) {
2215 return KERN_INVALID_ARGUMENT
;
2218 voucher
= convert_port_to_voucher(port
);
2222 kr
= mach_voucher_extract_all_attr_recipes(voucher
, recipes
, in_out_size
);
2223 ipc_voucher_release(voucher
);
2227 return KERN_FAILURE
;
2232 * Routine: mach_voucher_attr_command
2234 * Invoke an attribute-specific command through this voucher.
2236 * The voucher layout, membership, etc... is not altered
2237 * through the execution of this command.
2240 * Nothing locked - as it may upcall to user-space.
2241 * The caller holds a reference on the voucher.
2244 mach_voucher_attr_command(
2245 ipc_voucher_t voucher
,
2246 mach_voucher_attr_key_t key
,
2247 mach_voucher_attr_command_t command
,
2248 mach_voucher_attr_content_t in_content
,
2249 mach_voucher_attr_content_size_t in_content_size
,
2250 mach_voucher_attr_content_t out_content
,
2251 mach_voucher_attr_content_size_t
*out_content_size
)
2253 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2254 mach_voucher_attr_value_handle_array_size_t vals_count
;
2255 ipc_voucher_attr_manager_t manager
;
2256 ipc_voucher_attr_control_t control
;
2257 iv_index_t value_index
;
2258 iv_index_t key_index
;
2262 if (IV_NULL
== voucher
) {
2263 return KERN_INVALID_ARGUMENT
;
2266 key_index
= iv_key_to_index(key
);
2269 * Get the manager for this key_index.
2270 * Allowing commands against the default value
2271 * for an attribute means that we have to hold
2272 * reference on the attribute manager control
2273 * to keep the manager around during the command
2276 ivgt_lookup(key_index
, TRUE
, &manager
, &control
);
2277 if (IVAM_NULL
== manager
) {
2278 return KERN_INVALID_ARGUMENT
;
2282 * Get the values for this <voucher, key> pair
2283 * to pass to the attribute manager. It is still
2284 * permissible to execute a command against the
2285 * default value (empty value array).
2287 value_index
= iv_lookup(voucher
, key_index
);
2288 ivace_lookup_values(key_index
, value_index
,
2291 /* callout to manager */
2292 kr
= (manager
->ivam_command
)(manager
, key
,
2295 in_content
, in_content_size
,
2296 out_content
, out_content_size
);
2298 /* release reference on control */
2299 ivac_release(control
);
2305 * Routine: mach_voucher_attr_control_get_values
2307 * For a given voucher, get the value handle associated with the
2308 * specified attribute manager.
2311 mach_voucher_attr_control_get_values(
2312 ipc_voucher_attr_control_t control
,
2313 ipc_voucher_t voucher
,
2314 mach_voucher_attr_value_handle_array_t out_values
,
2315 mach_voucher_attr_value_handle_array_size_t
*in_out_size
)
2317 iv_index_t key_index
, value_index
;
2319 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
2320 return KERN_INVALID_CAPABILITY
;
2323 if (IV_NULL
== voucher
) {
2324 return KERN_INVALID_ARGUMENT
;
2327 if (0 == *in_out_size
) {
2328 return KERN_SUCCESS
;
2331 key_index
= control
->ivac_key_index
;
2333 assert(os_ref_get_count(&voucher
->iv_refs
) > 0);
2334 value_index
= iv_lookup(voucher
, key_index
);
2335 ivace_lookup_values(key_index
, value_index
,
2336 out_values
, in_out_size
);
2337 return KERN_SUCCESS
;
2341 * Routine: mach_voucher_attr_control_create_mach_voucher
2343 * Create a new mach voucher and initialize it by processing the
2344 * supplied recipe(s).
2346 * Coming in on the attribute control port denotes special privileges
2347 * over they key associated with the control port.
2349 * Coming in from user-space, each recipe item will have a previous
2350 * recipe port name that needs to be converted to a voucher. Because
2351 * we can't rely on the port namespace to hold a reference on each
2352 * previous voucher port for the duration of processing that command,
2353 * we have to convert the name to a voucher reference and release it
2354 * after the command processing is done.
2357 mach_voucher_attr_control_create_mach_voucher(
2358 ipc_voucher_attr_control_t control
,
2359 mach_voucher_attr_raw_recipe_array_t recipes
,
2360 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2361 ipc_voucher_t
*new_voucher
)
2363 mach_voucher_attr_key_t control_key
;
2364 mach_voucher_attr_recipe_t sub_recipe
;
2365 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2366 ipc_voucher_t voucher
= IV_NULL
;
2367 kern_return_t kr
= KERN_SUCCESS
;
2369 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
2370 return KERN_INVALID_CAPABILITY
;
2373 /* if nothing to do ... */
2374 if (0 == recipe_size
) {
2375 *new_voucher
= IV_NULL
;
2376 return KERN_SUCCESS
;
2379 /* allocate new voucher */
2380 voucher
= iv_alloc(ivgt_keys_in_use
);
2381 if (IV_NULL
== voucher
) {
2382 return KERN_RESOURCE_SHORTAGE
;
2385 control_key
= iv_index_to_key(control
->ivac_key_index
);
2387 /* iterate over the recipe items */
2388 while (0 < recipe_size
- recipe_used
) {
2389 ipc_voucher_t prev_iv
;
2391 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2392 kr
= KERN_INVALID_ARGUMENT
;
2396 /* find the next recipe */
2397 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2398 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2399 kr
= KERN_INVALID_ARGUMENT
;
2402 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2404 /* convert voucher port name (current space) into a voucher reference */
2405 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2406 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2407 kr
= KERN_INVALID_CAPABILITY
;
2411 kr
= ipc_execute_voucher_recipe_command(voucher
,
2413 sub_recipe
->command
,
2415 sub_recipe
->content
,
2416 sub_recipe
->content_size
,
2417 (sub_recipe
->key
== control_key
));
2418 ipc_voucher_release(prev_iv
);
2420 if (KERN_SUCCESS
!= kr
) {
2425 if (KERN_SUCCESS
== kr
) {
2426 *new_voucher
= iv_dedup(voucher
);
2428 *new_voucher
= IV_NULL
;
2429 iv_dealloc(voucher
, FALSE
);
2435 * Routine: host_create_mach_voucher
2437 * Create a new mach voucher and initialize it by processing the
2438 * supplied recipe(s).
2440 * Comming in from user-space, each recipe item will have a previous
2441 * recipe port name that needs to be converted to a voucher. Because
2442 * we can't rely on the port namespace to hold a reference on each
2443 * previous voucher port for the duration of processing that command,
2444 * we have to convert the name to a voucher reference and release it
2445 * after the command processing is done.
2448 host_create_mach_voucher(
2450 mach_voucher_attr_raw_recipe_array_t recipes
,
2451 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2452 ipc_voucher_t
*new_voucher
)
2454 mach_voucher_attr_recipe_t sub_recipe
;
2455 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2456 ipc_voucher_t voucher
= IV_NULL
;
2457 kern_return_t kr
= KERN_SUCCESS
;
2459 if (host
== HOST_NULL
) {
2460 return KERN_INVALID_ARGUMENT
;
2463 /* if nothing to do ... */
2464 if (0 == recipe_size
) {
2465 *new_voucher
= IV_NULL
;
2466 return KERN_SUCCESS
;
2469 /* allocate new voucher */
2470 voucher
= iv_alloc(ivgt_keys_in_use
);
2471 if (IV_NULL
== voucher
) {
2472 return KERN_RESOURCE_SHORTAGE
;
2475 /* iterate over the recipe items */
2476 while (0 < recipe_size
- recipe_used
) {
2477 ipc_voucher_t prev_iv
;
2479 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2480 kr
= KERN_INVALID_ARGUMENT
;
2484 /* find the next recipe */
2485 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2486 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2487 kr
= KERN_INVALID_ARGUMENT
;
2490 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2492 /* convert voucher port name (current space) into a voucher reference */
2493 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2494 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2495 kr
= KERN_INVALID_CAPABILITY
;
2499 kr
= ipc_execute_voucher_recipe_command(voucher
,
2501 sub_recipe
->command
,
2503 sub_recipe
->content
,
2504 sub_recipe
->content_size
,
2506 ipc_voucher_release(prev_iv
);
2508 if (KERN_SUCCESS
!= kr
) {
2513 if (KERN_SUCCESS
== kr
) {
2514 *new_voucher
= iv_dedup(voucher
);
2516 *new_voucher
= IV_NULL
;
2517 iv_dealloc(voucher
, FALSE
);
2523 * Routine: host_register_well_known_mach_voucher_attr_manager
2525 * Register the user-level resource manager responsible for a given
2528 * The manager port passed in has to be converted/wrapped
2529 * in an ipc_voucher_attr_manager_t structure and then call the
2530 * internal variant. We have a generic ipc voucher manager
2531 * type that implements a MIG proxy out to user-space just for
2535 host_register_well_known_mach_voucher_attr_manager(
2537 mach_voucher_attr_manager_t __unused manager
,
2538 mach_voucher_attr_value_handle_t __unused default_value
,
2539 mach_voucher_attr_key_t __unused key
,
2540 ipc_voucher_attr_control_t __unused
*control
)
2542 if (HOST_NULL
== host
) {
2543 return KERN_INVALID_HOST
;
2547 return KERN_NOT_SUPPORTED
;
2550 * Allocate a mig_voucher_attr_manager_t that provides the
2551 * MIG proxy functions for the three manager callbacks and
2552 * store the port right in there.
2554 * If the user-space manager dies, we'll detect it on our
2555 * next upcall, and cleanup the proxy at that point.
2557 mig_voucher_attr_manager_t proxy
;
2560 proxy
= mvam_alloc(manager
);
2562 kr
= ipc_register_well_known_mach_voucher_attr_manager(&proxy
->mvam_manager
,
2566 if (KERN_SUCCESS
!= kr
) {
2567 mvam_release(proxy
);
2575 * Routine: host_register_mach_voucher_attr_manager
2577 * Register the user-space resource manager and return a
2578 * dynamically allocated key.
2580 * Wrap the supplied port with the MIG proxy ipc
2581 * voucher resource manager, and then call the internal
2585 host_register_mach_voucher_attr_manager(
2587 mach_voucher_attr_manager_t __unused manager
,
2588 mach_voucher_attr_value_handle_t __unused default_value
,
2589 mach_voucher_attr_key_t __unused
*key
,
2590 ipc_voucher_attr_control_t __unused
*control
)
2592 if (HOST_NULL
== host
) {
2593 return KERN_INVALID_HOST
;
2596 return KERN_NOT_SUPPORTED
;
2600 * Routine: ipc_get_pthpriority_from_kmsg_voucher
2602 * Get the canonicalized pthread priority from the voucher attached in the kmsg.
2605 ipc_get_pthpriority_from_kmsg_voucher(
2607 ipc_pthread_priority_value_t
*canonicalize_priority_value
)
2609 ipc_voucher_t pthread_priority_voucher
;
2610 mach_voucher_attr_raw_recipe_size_t content_size
=
2611 sizeof(mach_voucher_attr_recipe_data_t
) + sizeof(ipc_pthread_priority_value_t
);
2612 uint8_t content_data
[content_size
];
2613 mach_voucher_attr_recipe_t cur_content
;
2614 kern_return_t kr
= KERN_SUCCESS
;
2616 if (!IP_VALID(kmsg
->ikm_voucher
)) {
2617 return KERN_FAILURE
;
2620 pthread_priority_voucher
= ip_get_voucher(kmsg
->ikm_voucher
);
2621 kr
= mach_voucher_extract_attr_recipe(pthread_priority_voucher
,
2622 MACH_VOUCHER_ATTR_KEY_PTHPRIORITY
,
2625 if (kr
!= KERN_SUCCESS
) {
2629 /* return KERN_INVALID_VALUE for default value */
2630 if (content_size
< sizeof(mach_voucher_attr_recipe_t
)) {
2631 return KERN_INVALID_VALUE
;
2634 cur_content
= (mach_voucher_attr_recipe_t
) (void *) &content_data
[0];
2635 assert(cur_content
->content_size
== sizeof(ipc_pthread_priority_value_t
));
2636 memcpy(canonicalize_priority_value
, cur_content
->content
, sizeof(ipc_pthread_priority_value_t
));
2638 return KERN_SUCCESS
;
2643 * Routine: ipc_voucher_send_preprocessing
2645 * Processing of the voucher in the kmsg before sending it.
2646 * Currently use to switch PERSONA_TOKEN in case of process with
2647 * no com.apple.private.personas.propagate entitlement.
2650 ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg
)
2652 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2653 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2654 sizeof(ipc_voucher_attr_recipe_data_t
);
2655 ipc_voucher_t pre_processed_voucher
;
2656 ipc_voucher_t voucher_to_send
;
2658 int need_preprocessing
= FALSE
;
2660 if (!IP_VALID(kmsg
->ikm_voucher
) || current_task() == kernel_task
) {
2664 /* setup recipe for preprocessing of all the attributes. */
2665 pre_processed_voucher
= ip_get_voucher(kmsg
->ikm_voucher
);
2667 kr
= ipc_voucher_prepare_processing_recipe(pre_processed_voucher
,
2668 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2669 &recipe_size
, MACH_VOUCHER_ATTR_SEND_PREPROCESS
,
2670 IVAM_FLAGS_SUPPORT_SEND_PREPROCESS
, &need_preprocessing
);
2672 assert(KERN_SUCCESS
== kr
);
2674 * Only do send preprocessing if the voucher needs any pre processing.
2676 if (need_preprocessing
) {
2677 kr
= ipc_create_mach_voucher(recipes
,
2680 assert(KERN_SUCCESS
== kr
);
2681 ipc_port_release_send(kmsg
->ikm_voucher
);
2682 kmsg
->ikm_voucher
= convert_voucher_to_port(voucher_to_send
);
2687 * Routine: ipc_voucher_receive_postprocessing
2689 * Redeems the voucher attached to the kmsg.
2691 * Although it is possible to call ipc_importance_receive
2692 * here, it is called in mach_msg_receive_results and not here
2693 * in order to maintain symmetry with ipc_voucher_send_preprocessing.
2696 ipc_voucher_receive_postprocessing(
2698 mach_msg_option_t option
)
2700 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2701 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2702 sizeof(ipc_voucher_attr_recipe_data_t
);
2703 ipc_voucher_t recv_voucher
;
2704 ipc_voucher_t sent_voucher
;
2706 int need_postprocessing
= FALSE
;
2708 if ((option
& MACH_RCV_VOUCHER
) == 0 || (!IP_VALID(kmsg
->ikm_voucher
)) ||
2709 current_task() == kernel_task
) {
2713 /* setup recipe for auto redeem of all the attributes. */
2714 sent_voucher
= ip_get_voucher(kmsg
->ikm_voucher
);
2716 kr
= ipc_voucher_prepare_processing_recipe(sent_voucher
,
2717 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2718 &recipe_size
, MACH_VOUCHER_ATTR_AUTO_REDEEM
,
2719 IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS
, &need_postprocessing
);
2721 assert(KERN_SUCCESS
== kr
);
2724 * Only do receive postprocessing if the voucher needs any post processing.
2726 if (need_postprocessing
) {
2727 kr
= ipc_create_mach_voucher(recipes
,
2730 assert(KERN_SUCCESS
== kr
);
2731 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
2732 kmsg
->ikm_header
->msgh_bits
|= (MACH_MSG_TYPE_MOVE_SEND
<< 16);
2733 ipc_port_release_send(kmsg
->ikm_voucher
);
2734 kmsg
->ikm_voucher
= convert_voucher_to_port(recv_voucher
);
2739 * Routine: ipc_voucher_prepare_processing_recipe
2741 * Check if the given voucher has an attribute which supports
2742 * the given flag and prepare a recipe to apply that supported
2745 static kern_return_t
2746 ipc_voucher_prepare_processing_recipe(
2747 ipc_voucher_t voucher
,
2748 ipc_voucher_attr_raw_recipe_array_t recipes
,
2749 ipc_voucher_attr_raw_recipe_array_size_t
*in_out_size
,
2750 mach_voucher_attr_recipe_command_t command
,
2751 ipc_voucher_attr_manager_flags flags
,
2752 int *need_processing
)
2754 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= *in_out_size
;
2755 ipc_voucher_attr_raw_recipe_array_size_t recipe_used
= 0;
2756 iv_index_t key_index
;
2757 ipc_voucher_attr_recipe_t recipe
;
2759 if (IV_NULL
== voucher
) {
2760 return KERN_INVALID_ARGUMENT
;
2763 /* Setup a recipe to copy all attributes. */
2764 if (recipe_size
< sizeof(*recipe
)) {
2765 return KERN_NO_SPACE
;
2768 *need_processing
= FALSE
;
2769 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2770 recipe
->key
= MACH_VOUCHER_ATTR_KEY_ALL
;
2771 recipe
->command
= MACH_VOUCHER_ATTR_COPY
;
2772 recipe
->previous_voucher
= voucher
;
2773 recipe
->content_size
= 0;
2774 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2776 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2777 ipc_voucher_attr_manager_t manager
;
2778 mach_voucher_attr_key_t key
;
2779 iv_index_t value_index
;
2781 /* don't output anything for a default value */
2782 value_index
= iv_lookup(voucher
, key_index
);
2783 if (IV_UNUSED_VALINDEX
== value_index
) {
2787 if (recipe_size
- recipe_used
< sizeof(*recipe
)) {
2788 return KERN_NO_SPACE
;
2791 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2794 * Get the manager for this key_index. The
2795 * existence of a non-default value for this
2796 * slot within our voucher will keep the
2797 * manager referenced during the callout.
2799 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2800 assert(IVAM_NULL
!= manager
);
2801 if (IVAM_NULL
== manager
) {
2805 /* Check if the supported flag is set in the manager */
2806 if ((manager
->ivam_flags
& flags
) == 0) {
2810 key
= iv_index_to_key(key_index
);
2813 recipe
->command
= command
;
2814 recipe
->content_size
= 0;
2815 recipe
->previous_voucher
= voucher
;
2817 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2818 *need_processing
= TRUE
;
2821 *in_out_size
= recipe_used
;
2822 return KERN_SUCCESS
;
2826 * Activity id Generation
2828 uint64_t voucher_activity_id
;
2830 #define generate_activity_id(x) \
2831 ((uint64_t)OSAddAtomic64((x), (int64_t *)&voucher_activity_id))
2834 * Routine: mach_init_activity_id
2836 * Initialize voucher activity id.
2839 mach_init_activity_id(void)
2841 voucher_activity_id
= 1;
2845 * Routine: mach_generate_activity_id
2847 * Generate a system wide voucher activity id.
2850 mach_generate_activity_id(
2851 struct mach_generate_activity_id_args
*args
)
2853 uint64_t activity_id
;
2854 kern_return_t kr
= KERN_SUCCESS
;
2856 if (args
->count
<= 0 || args
->count
> MACH_ACTIVITY_ID_COUNT_MAX
) {
2857 return KERN_INVALID_ARGUMENT
;
2860 activity_id
= generate_activity_id(args
->count
);
2861 kr
= copyout(&activity_id
, args
->activity_id
, sizeof(activity_id
));
2866 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2869 * Build-in a simple User Data Resource Manager
2871 #define USER_DATA_MAX_DATA (16*1024)
2873 struct user_data_value_element
{
2874 mach_voucher_attr_value_reference_t e_made
;
2875 mach_voucher_attr_content_size_t e_size
;
2878 queue_chain_t e_hash_link
;
2882 typedef struct user_data_value_element
*user_data_element_t
;
2885 * User Data Voucher Hash Table
2887 #define USER_DATA_HASH_BUCKETS 127
2888 #define USER_DATA_HASH_BUCKET(x) ((x) % USER_DATA_HASH_BUCKETS)
2890 static queue_head_t user_data_bucket
[USER_DATA_HASH_BUCKETS
];
2891 static LCK_SPIN_DECLARE_ATTR(user_data_lock_data
, &ipc_lck_grp
, &ipc_lck_attr
);
2893 #define user_data_lock_destroy() \
2894 lck_spin_destroy(&user_data_lock_data, &ipc_lck_grp)
2895 #define user_data_lock() \
2896 lck_spin_lock_grp(&user_data_lock_data, &ipc_lck_grp)
2897 #define user_data_lock_try() \
2898 lck_spin_try_lock_grp(&user_data_lock_data, &ipc_lck_grp)
2899 #define user_data_unlock() \
2900 lck_spin_unlock(&user_data_lock_data)
2902 static kern_return_t
2903 user_data_release_value(
2904 ipc_voucher_attr_manager_t manager
,
2905 mach_voucher_attr_key_t key
,
2906 mach_voucher_attr_value_handle_t value
,
2907 mach_voucher_attr_value_reference_t sync
);
2909 static kern_return_t
2910 user_data_get_value(
2911 ipc_voucher_attr_manager_t manager
,
2912 mach_voucher_attr_key_t key
,
2913 mach_voucher_attr_recipe_command_t command
,
2914 mach_voucher_attr_value_handle_array_t prev_values
,
2915 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
2916 mach_voucher_attr_content_t content
,
2917 mach_voucher_attr_content_size_t content_size
,
2918 mach_voucher_attr_value_handle_t
*out_value
,
2919 mach_voucher_attr_value_flags_t
*out_flags
,
2920 ipc_voucher_t
*out_value_voucher
);
2922 static kern_return_t
2923 user_data_extract_content(
2924 ipc_voucher_attr_manager_t manager
,
2925 mach_voucher_attr_key_t key
,
2926 mach_voucher_attr_value_handle_array_t values
,
2927 mach_voucher_attr_value_handle_array_size_t value_count
,
2928 mach_voucher_attr_recipe_command_t
*out_command
,
2929 mach_voucher_attr_content_t out_content
,
2930 mach_voucher_attr_content_size_t
*in_out_content_size
);
2932 static kern_return_t
2934 ipc_voucher_attr_manager_t manager
,
2935 mach_voucher_attr_key_t key
,
2936 mach_voucher_attr_value_handle_array_t values
,
2937 mach_msg_type_number_t value_count
,
2938 mach_voucher_attr_command_t command
,
2939 mach_voucher_attr_content_t in_content
,
2940 mach_voucher_attr_content_size_t in_content_size
,
2941 mach_voucher_attr_content_t out_content
,
2942 mach_voucher_attr_content_size_t
*out_content_size
);
2946 ipc_voucher_attr_manager_t manager
);
2948 const struct ipc_voucher_attr_manager user_data_manager
= {
2949 .ivam_release_value
= user_data_release_value
,
2950 .ivam_get_value
= user_data_get_value
,
2951 .ivam_extract_content
= user_data_extract_content
,
2952 .ivam_command
= user_data_command
,
2953 .ivam_release
= user_data_release
,
2954 .ivam_flags
= IVAM_FLAGS_NONE
,
2957 ipc_voucher_attr_control_t user_data_control
;
2958 ipc_voucher_attr_control_t test_control
;
2960 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) && defined(MACH_VOUCHER_ATTR_KEY_TEST)
2961 #define USER_DATA_ASSERT_KEY(key) \
2962 assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key) || \
2963 MACH_VOUCHER_ATTR_KEY_TEST == (key));
2964 #elif defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
2965 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key))
2967 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_TEST == (key))
2971 * Routine: user_data_release_value
2973 * Release a made reference on a specific value managed by
2974 * this voucher attribute manager.
2976 * Must remove the element associated with this value from
2977 * the hash if this is the last know made reference.
2979 static kern_return_t
2980 user_data_release_value(
2981 ipc_voucher_attr_manager_t __assert_only manager
,
2982 mach_voucher_attr_key_t __assert_only key
,
2983 mach_voucher_attr_value_handle_t value
,
2984 mach_voucher_attr_value_reference_t sync
)
2986 user_data_element_t elem
;
2989 assert(&user_data_manager
== manager
);
2990 USER_DATA_ASSERT_KEY(key
);
2992 elem
= (user_data_element_t
)value
;
2993 hash
= elem
->e_hash
;
2996 if (sync
== elem
->e_made
) {
2997 queue_remove(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
);
2999 kfree(elem
, sizeof(*elem
) + elem
->e_size
);
3000 return KERN_SUCCESS
;
3002 assert(sync
< elem
->e_made
);
3005 return KERN_FAILURE
;
3009 * Routine: user_data_checksum
3011 * Provide a rudimentary checksum for the data presented
3012 * to these voucher attribute managers.
3016 mach_voucher_attr_content_t content
,
3017 mach_voucher_attr_content_size_t content_size
)
3019 mach_voucher_attr_content_size_t i
;
3020 iv_index_t cksum
= 0;
3022 for (i
= 0; i
< content_size
; i
++, content
++) {
3023 cksum
= (cksum
<< 8) ^ (cksum
+ *(unsigned char *)content
);
3030 * Routine: user_data_dedup
3032 * See if the content represented by this request already exists
3033 * in another user data element. If so return a made reference
3034 * to the existing element. Otherwise, create a new element and
3035 * return that (after inserting it in the hash).
3039 * A made reference on the user_data_element_t
3041 static user_data_element_t
3043 mach_voucher_attr_content_t content
,
3044 mach_voucher_attr_content_size_t content_size
)
3048 user_data_element_t elem
;
3049 user_data_element_t alloc
= NULL
;
3051 sum
= user_data_checksum(content
, content_size
);
3052 hash
= USER_DATA_HASH_BUCKET(sum
);
3056 queue_iterate(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
) {
3057 assert(elem
->e_hash
== hash
);
3059 /* if sums match... */
3060 if (elem
->e_sum
== sum
&& elem
->e_size
== content_size
) {
3063 /* and all data matches */
3064 for (i
= 0; i
< content_size
; i
++) {
3065 if (elem
->e_data
[i
] != content
[i
]) {
3069 if (i
< content_size
) {
3073 /* ... we found a match... */
3078 if (NULL
!= alloc
) {
3079 kfree(alloc
, sizeof(*alloc
) + content_size
);
3086 if (NULL
== alloc
) {
3089 alloc
= (user_data_element_t
)kalloc(sizeof(*alloc
) + content_size
);
3091 alloc
->e_size
= content_size
;
3093 alloc
->e_hash
= hash
;
3094 memcpy(alloc
->e_data
, content
, content_size
);
3098 queue_enter(&user_data_bucket
[hash
], alloc
, user_data_element_t
, e_hash_link
);
3104 static kern_return_t
3105 user_data_get_value(
3106 ipc_voucher_attr_manager_t __assert_only manager
,
3107 mach_voucher_attr_key_t __assert_only key
,
3108 mach_voucher_attr_recipe_command_t command
,
3109 mach_voucher_attr_value_handle_array_t prev_values
,
3110 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3111 mach_voucher_attr_content_t content
,
3112 mach_voucher_attr_content_size_t content_size
,
3113 mach_voucher_attr_value_handle_t
*out_value
,
3114 mach_voucher_attr_value_flags_t
*out_flags
,
3115 ipc_voucher_t
*out_value_voucher
)
3117 user_data_element_t elem
;
3119 assert(&user_data_manager
== manager
);
3120 USER_DATA_ASSERT_KEY(key
);
3122 /* never an out voucher */
3123 *out_value_voucher
= IPC_VOUCHER_NULL
;
3124 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
3127 case MACH_VOUCHER_ATTR_REDEEM
:
3129 /* redeem of previous values is the value */
3130 if (0 < prev_value_count
) {
3131 elem
= (user_data_element_t
)prev_values
[0];
3134 assert(0 < elem
->e_made
);
3138 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
3139 return KERN_SUCCESS
;
3142 /* redeem of default is default */
3144 return KERN_SUCCESS
;
3146 case MACH_VOUCHER_ATTR_USER_DATA_STORE
:
3147 if (USER_DATA_MAX_DATA
< content_size
) {
3148 return KERN_RESOURCE_SHORTAGE
;
3151 /* empty is the default */
3152 if (0 == content_size
) {
3154 return KERN_SUCCESS
;
3157 elem
= user_data_dedup(content
, content_size
);
3158 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
3159 return KERN_SUCCESS
;
3162 /* every other command is unknown */
3163 return KERN_INVALID_ARGUMENT
;
3167 static kern_return_t
3168 user_data_extract_content(
3169 ipc_voucher_attr_manager_t __assert_only manager
,
3170 mach_voucher_attr_key_t __assert_only key
,
3171 mach_voucher_attr_value_handle_array_t values
,
3172 mach_voucher_attr_value_handle_array_size_t value_count
,
3173 mach_voucher_attr_recipe_command_t
*out_command
,
3174 mach_voucher_attr_content_t out_content
,
3175 mach_voucher_attr_content_size_t
*in_out_content_size
)
3177 mach_voucher_attr_content_size_t size
= 0;
3178 user_data_element_t elem
;
3181 assert(&user_data_manager
== manager
);
3182 USER_DATA_ASSERT_KEY(key
);
3184 /* concatenate the stored data items */
3185 for (i
= 0; i
< value_count
&& *in_out_content_size
> 0; i
++) {
3186 elem
= (user_data_element_t
)values
[i
];
3187 assert(USER_DATA_MAX_DATA
>= elem
->e_size
);
3189 if (size
+ elem
->e_size
> *in_out_content_size
) {
3190 return KERN_NO_SPACE
;
3193 memcpy(&out_content
[size
], elem
->e_data
, elem
->e_size
);
3194 size
+= elem
->e_size
;
3196 *out_command
= MACH_VOUCHER_ATTR_BITS_STORE
;
3197 *in_out_content_size
= size
;
3198 return KERN_SUCCESS
;
3201 static kern_return_t
3203 ipc_voucher_attr_manager_t __assert_only manager
,
3204 mach_voucher_attr_key_t __assert_only key
,
3205 mach_voucher_attr_value_handle_array_t __unused values
,
3206 mach_msg_type_number_t __unused value_count
,
3207 mach_voucher_attr_command_t __unused command
,
3208 mach_voucher_attr_content_t __unused in_content
,
3209 mach_voucher_attr_content_size_t __unused in_content_size
,
3210 mach_voucher_attr_content_t __unused out_content
,
3211 mach_voucher_attr_content_size_t __unused
*out_content_size
)
3213 assert(&user_data_manager
== manager
);
3214 USER_DATA_ASSERT_KEY(key
);
3215 return KERN_FAILURE
;
3220 ipc_voucher_attr_manager_t manager
)
3222 if (manager
!= &user_data_manager
) {
3226 panic("Voucher user-data manager released");
3231 user_data_attr_manager_init(void)
3235 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
3236 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3237 (mach_voucher_attr_value_handle_t
)0,
3238 MACH_VOUCHER_ATTR_KEY_USER_DATA
,
3239 &user_data_control
);
3240 if (KERN_SUCCESS
!= kr
) {
3241 printf("Voucher user-data manager register(USER-DATA) returned %d", kr
);
3244 #if defined(MACH_VOUCHER_ATTR_KEY_TEST)
3245 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3246 (mach_voucher_attr_value_handle_t
)0,
3247 MACH_VOUCHER_ATTR_KEY_TEST
,
3249 if (KERN_SUCCESS
!= kr
) {
3250 printf("Voucher user-data manager register(TEST) returned %d", kr
);
3253 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
3254 for (int i
= 0; i
< USER_DATA_HASH_BUCKETS
; i
++) {
3255 queue_init(&user_data_bucket
[i
]);
3259 STARTUP(MACH_IPC
, STARTUP_RANK_FIRST
, user_data_attr_manager_init
);
3261 #endif /* MACH_VOUCHER_ATTR_KEY_USER_DATA || MACH_VOUCHER_ATTR_KEY_TEST */