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
);
211 zone_change(ipc_voucher_zone
, Z_CLEARMEMORY
, TRUE
);
213 ipc_voucher_attr_control_zone
= zinit(sizeof(struct ipc_voucher_attr_control
),
214 attr_manager_max
* sizeof(struct ipc_voucher_attr_control
),
215 sizeof(struct ipc_voucher_attr_control
),
216 "ipc voucher attr controls");
217 zone_change(ipc_voucher_attr_control_zone
, Z_NOENCRYPT
, TRUE
);
218 zone_change(ipc_voucher_attr_control_zone
, Z_CLEARMEMORY
, TRUE
);
220 /* initialize voucher hash */
222 for (i
= 0; i
< IV_HASH_BUCKETS
; i
++) {
223 queue_init(&ivht_bucket
[i
]);
226 /* initialize global table locking */
229 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
230 user_data_attr_manager_init();
235 iv_alloc(iv_index_t entries
)
241 iv
= (ipc_voucher_t
)zalloc(ipc_voucher_zone
);
246 os_ref_init(&iv
->iv_refs
, &iv_refgrp
);
249 iv
->iv_port
= IP_NULL
;
251 if (entries
> IV_ENTRIES_INLINE
) {
254 /* TODO - switch to ipc_table method of allocation */
255 table
= (iv_entry_t
) kalloc(sizeof(*table
) * entries
);
256 if (IVE_NULL
== table
) {
257 zfree(ipc_voucher_zone
, iv
);
260 iv
->iv_table
= table
;
261 iv
->iv_table_size
= entries
;
263 iv
->iv_table
= iv
->iv_inline_table
;
264 iv
->iv_table_size
= IV_ENTRIES_INLINE
;
267 /* initialize the table entries */
268 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
269 iv
->iv_table
[i
] = IV_UNUSED_VALINDEX
;
278 * Set the voucher's value index for a given key index.
280 * This is only called during voucher creation, as
281 * they are immutable once references are distributed.
284 iv_set(ipc_voucher_t iv
,
285 iv_index_t key_index
,
286 iv_index_t value_index
)
288 assert(key_index
< iv
->iv_table_size
);
289 iv
->iv_table
[key_index
] = value_index
;
293 iv_dealloc(ipc_voucher_t iv
, boolean_t unhash
)
295 ipc_port_t port
= iv
->iv_port
;
299 * Do we have to remove it from the hash?
303 assert(os_ref_get_count(&iv
->iv_refs
) == 0);
304 assert(IV_HASH_BUCKETS
> iv
->iv_hash
);
305 queue_remove(&ivht_bucket
[iv
->iv_hash
], iv
, ipc_voucher_t
, iv_hash_link
);
309 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_DESTROY
) | DBG_FUNC_NONE
,
310 VM_KERNEL_ADDRPERM((uintptr_t)iv
), 0, ivht_count
, 0, 0);
312 os_ref_count_t cnt __assert_only
= os_ref_release(&iv
->iv_refs
);
317 * if a port was allocated for this voucher,
318 * it must not have any remaining send rights,
319 * because the port's reference on the voucher
320 * is gone. We can just discard it now.
322 if (IP_VALID(port
)) {
323 require_ip_active(port
);
324 assert(port
->ip_srights
== 0);
326 ipc_port_dealloc_kernel(port
);
329 /* release the attribute references held by this voucher */
330 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
331 ivace_release(i
, iv
->iv_table
[i
]);
337 if (iv
->iv_table
!= iv
->iv_inline_table
) {
339 iv
->iv_table_size
* sizeof(*iv
->iv_table
));
342 zfree(ipc_voucher_zone
, iv
);
348 * Find the voucher's value index for a given key_index
350 * Vouchers are immutable, so no locking required to do
353 static inline iv_index_t
354 iv_lookup(ipc_voucher_t iv
, iv_index_t key_index
)
356 if (key_index
< iv
->iv_table_size
) {
357 return iv
->iv_table
[key_index
];
359 return IV_UNUSED_VALINDEX
;
363 * Routine: unsafe_convert_port_to_voucher
365 * Unsafe conversion of a port to a voucher.
366 * Intended only for use by trace and debugging
367 * code. Consumes nothing, validates very little,
368 * produces an unreferenced voucher, which you
369 * MAY NOT use as a voucher, only log as an
372 * Caller has a send-right reference to port.
373 * Port may or may not be locked.
376 unsafe_convert_port_to_voucher(
379 if (IP_VALID(port
)) {
380 /* vouchers never labeled (they get transformed before use) */
381 if (ip_is_kolabeled(port
)) {
382 return (uintptr_t)IV_NULL
;
385 uintptr_t voucher
= (uintptr_t)port
->ip_kobject
;
388 * No need to lock because we have a reference on the
389 * port, and if it is a true voucher port, that reference
390 * keeps the voucher bound to the port (and active).
392 if (ip_kotype(port
) == IKOT_VOUCHER
) {
396 return (uintptr_t)IV_NULL
;
400 * Routine: convert_port_to_voucher
402 * Convert from a port to a voucher.
403 * Doesn't consume the port [send-right] ref;
404 * produces a voucher ref, which may be null.
406 * Caller has a send-right reference to port.
407 * Port may or may not be locked.
410 convert_port_to_voucher(
413 if (IP_VALID(port
)) {
414 zone_require(port
, ipc_object_zones
[IOT_PORT
]);
415 ipc_voucher_t voucher
= (ipc_voucher_t
) ip_get_kobject(port
);
418 * No need to lock because we have a reference on the
419 * port, and if it is a true voucher port, that reference
420 * keeps the voucher bound to the port (and active).
422 if (ip_kotype(port
) != IKOT_VOUCHER
) {
426 require_ip_active(port
);
428 zone_require(voucher
, ipc_voucher_zone
);
429 ipc_voucher_reference(voucher
);
436 * Routine: convert_port_name_to_voucher
438 * Convert from a port name in the current space to a voucher.
439 * Produces a voucher ref, which may be null.
445 convert_port_name_to_voucher(
446 mach_port_name_t voucher_name
)
452 if (MACH_PORT_VALID(voucher_name
)) {
453 kr
= ipc_port_translate_send(current_space(), voucher_name
, &port
);
454 if (KERN_SUCCESS
!= kr
) {
458 iv
= convert_port_to_voucher(port
);
467 ipc_voucher_reference(ipc_voucher_t voucher
)
469 if (IPC_VOUCHER_NULL
== voucher
) {
473 iv_reference(voucher
);
477 ipc_voucher_release(ipc_voucher_t voucher
)
479 if (IPC_VOUCHER_NULL
!= voucher
) {
485 * Routine: ipc_voucher_notify
487 * Called whenever the Mach port system detects no-senders
488 * on the voucher port.
491 ipc_voucher_notify(mach_msg_header_t
*msg
)
493 mach_no_senders_notification_t
*notification
= (void *)msg
;
494 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
495 ipc_voucher_t voucher
= (ipc_voucher_t
)ip_get_kobject(port
);
497 require_ip_active(port
);
498 assert(IKOT_VOUCHER
== ip_kotype(port
));
500 /* consume the reference donated by convert_voucher_to_port */
501 zone_require(voucher
, ipc_voucher_zone
);
502 ipc_voucher_release(voucher
);
506 * Convert a voucher to a port.
509 convert_voucher_to_port(ipc_voucher_t voucher
)
511 if (IV_NULL
== voucher
) {
515 zone_require(voucher
, ipc_voucher_zone
);
516 assert(os_ref_get_count(&voucher
->iv_refs
) > 0);
519 * make a send right and donate our reference for ipc_voucher_notify
520 * if this is the first send right
522 if (!ipc_kobject_make_send_lazy_alloc_port(&voucher
->iv_port
,
523 (ipc_kobject_t
)voucher
, IKOT_VOUCHER
)) {
524 ipc_voucher_release(voucher
);
526 return voucher
->iv_port
;
529 #define ivace_reset_data(ivace_elem, next_index) { \
530 (ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE; \
531 (ivace_elem)->ivace_refs = 0; \
532 (ivace_elem)->ivace_persist = 0; \
533 (ivace_elem)->ivace_made = 0; \
534 (ivace_elem)->ivace_free = TRUE; \
535 (ivace_elem)->ivace_releasing = FALSE; \
536 (ivace_elem)->ivace_layered = 0; \
537 (ivace_elem)->ivace_index = IV_HASH_END; \
538 (ivace_elem)->ivace_next = (next_index); \
541 #define ivace_copy_data(ivace_src_elem, ivace_dst_elem) { \
542 (ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
543 (ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs; \
544 (ivace_dst_elem)->ivace_persist = (ivace_src_elem)->ivace_persist; \
545 (ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made; \
546 (ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free; \
547 (ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered; \
548 (ivace_dst_elem)->ivace_releasing = (ivace_src_elem)->ivace_releasing; \
549 (ivace_dst_elem)->ivace_index = (ivace_src_elem)->ivace_index; \
550 (ivace_dst_elem)->ivace_next = (ivace_src_elem)->ivace_next; \
553 ipc_voucher_attr_control_t
554 ivac_alloc(iv_index_t key_index
)
556 ipc_voucher_attr_control_t ivac
;
561 ivac
= (ipc_voucher_attr_control_t
)zalloc(ipc_voucher_attr_control_zone
);
562 if (IVAC_NULL
== ivac
) {
566 os_ref_init(&ivac
->ivac_refs
, &ivac_refgrp
);
567 ivac
->ivac_is_growing
= FALSE
;
568 ivac
->ivac_port
= IP_NULL
;
570 /* start with just the inline table */
571 table
= (ivac_entry_t
) kalloc(IVAC_ENTRIES_MIN
* sizeof(ivac_entry
));
572 ivac
->ivac_table
= table
;
573 ivac
->ivac_table_size
= IVAC_ENTRIES_MIN
;
574 ivac
->ivac_init_table_size
= IVAC_ENTRIES_MIN
;
575 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
576 ivace_reset_data(&table
[i
], i
+ 1);
579 /* the default table entry is never on freelist */
580 table
[0].ivace_next
= IV_HASH_END
;
581 table
[0].ivace_free
= FALSE
;
582 table
[i
- 1].ivace_next
= IV_FREELIST_END
;
583 ivac
->ivac_freelist
= 1;
584 ivac_lock_init(ivac
);
585 ivac
->ivac_key_index
= key_index
;
591 ivac_dealloc(ipc_voucher_attr_control_t ivac
)
593 ipc_voucher_attr_manager_t ivam
= IVAM_NULL
;
594 iv_index_t key_index
= ivac
->ivac_key_index
;
595 ipc_port_t port
= ivac
->ivac_port
;
599 * If the control is in the global table, we
600 * have to remove it from there before we (re)confirm
601 * that the reference count is still zero.
604 if (os_ref_get_count(&ivac
->ivac_refs
) > 0) {
609 /* take it out of the global table */
610 if (iv_global_table
[key_index
].ivgte_control
== ivac
) {
611 ivam
= iv_global_table
[key_index
].ivgte_manager
;
612 iv_global_table
[key_index
].ivgte_manager
= IVAM_NULL
;
613 iv_global_table
[key_index
].ivgte_control
= IVAC_NULL
;
614 iv_global_table
[key_index
].ivgte_key
= MACH_VOUCHER_ATTR_KEY_NONE
;
618 /* release the reference held on the resource manager */
619 if (IVAM_NULL
!= ivam
) {
620 (ivam
->ivam_release
)(ivam
);
624 * if a port was allocated for this voucher,
625 * it must not have any remaining send rights,
626 * because the port's reference on the voucher
627 * is gone. We can just discard it now.
629 if (IP_VALID(port
)) {
630 require_ip_active(port
);
631 assert(port
->ip_srights
== 0);
633 ipc_port_dealloc_kernel(port
);
637 * the resource manager's control reference and all references
638 * held by the specific value caches are gone, so free the
642 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
643 if (ivac
->ivac_table
[i
].ivace_refs
!= 0) {
644 panic("deallocing a resource manager with live refs to its attr values\n");
648 kfree(ivac
->ivac_table
, ivac
->ivac_table_size
* sizeof(*ivac
->ivac_table
));
649 ivac_lock_destroy(ivac
);
650 zfree(ipc_voucher_attr_control_zone
, ivac
);
654 ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control
)
656 ivac_reference(control
);
660 ipc_voucher_attr_control_release(ipc_voucher_attr_control_t control
)
662 ivac_release(control
);
666 * Routine: convert_port_to_voucher_attr_control reference
668 * Convert from a port to a voucher attribute control.
669 * Doesn't consume the port ref; produces a voucher ref,
674 ipc_voucher_attr_control_t
675 convert_port_to_voucher_attr_control(
678 if (IP_VALID(port
)) {
679 zone_require(port
, ipc_object_zones
[IOT_PORT
]);
680 ipc_voucher_attr_control_t ivac
= (ipc_voucher_attr_control_t
) ip_get_kobject(port
);
683 * No need to lock because we have a reference on the
684 * port, and if it is a true voucher control port,
685 * that reference keeps the voucher bound to the port
688 if (ip_kotype(port
) != IKOT_VOUCHER_ATTR_CONTROL
) {
691 require_ip_active(port
);
693 zone_require(ivac
, ipc_voucher_attr_control_zone
);
694 ivac_reference(ivac
);
701 * Routine: ipc_voucher_notify
703 * Called whenever the Mach port system detects no-senders
704 * on the voucher attr control port.
707 ipc_voucher_attr_control_notify(mach_msg_header_t
*msg
)
709 mach_no_senders_notification_t
*notification
= (void *)msg
;
710 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
711 ipc_voucher_attr_control_t ivac
;
713 require_ip_active(port
);
714 assert(IKOT_VOUCHER_ATTR_CONTROL
== ip_kotype(port
));
716 /* release the reference donated by convert_voucher_attr_control_to_port */
717 ivac
= (ipc_voucher_attr_control_t
)ip_get_kobject(port
);
718 zone_require(ivac
, ipc_voucher_attr_control_zone
);
723 * Convert a voucher attr control to a port.
726 convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control
)
728 if (IVAC_NULL
== control
) {
732 zone_require(control
, ipc_voucher_attr_control_zone
);
735 * make a send right and donate our reference for
736 * ipc_voucher_attr_control_notify if this is the first send right
738 if (!ipc_kobject_make_send_lazy_alloc_port(&control
->ivac_port
,
739 (ipc_kobject_t
)control
, IKOT_VOUCHER_ATTR_CONTROL
)) {
740 ivac_release(control
);
742 return control
->ivac_port
;
746 * Look up the values for a given <key, index> pair.
750 iv_index_t key_index
,
751 iv_index_t value_index
,
752 mach_voucher_attr_value_handle_array_t values
,
753 mach_voucher_attr_value_handle_array_size_t
*count
)
755 ipc_voucher_attr_control_t ivac
;
758 if (IV_UNUSED_VALINDEX
== value_index
||
759 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
<= key_index
) {
764 ivac
= iv_global_table
[key_index
].ivgte_control
;
765 assert(IVAC_NULL
!= ivac
);
768 * Get the entry and then the linked values.
771 assert(value_index
< ivac
->ivac_table_size
);
772 ivace
= &ivac
->ivac_table
[value_index
];
775 * TODO: support chained values (for effective vouchers).
777 assert(ivace
->ivace_refs
> 0);
778 values
[0] = ivace
->ivace_value
;
784 * ivac_grow_table - Allocate a bigger table of attribute values
786 * Conditions: ivac is locked on entry and again on return
789 ivac_grow_table(ipc_voucher_attr_control_t ivac
)
793 /* NOTE: do not modify *_table and *_size values once set */
794 ivac_entry_t new_table
= NULL
, old_table
= NULL
;
795 iv_index_t new_size
, old_size
;
797 if (ivac
->ivac_is_growing
) {
802 ivac
->ivac_is_growing
= 1;
803 if (ivac
->ivac_table_size
>= IVAC_ENTRIES_MAX
) {
804 panic("Cannot grow ipc space beyond IVAC_ENTRIES_MAX. Some process is leaking vouchers");
808 old_size
= ivac
->ivac_table_size
;
811 new_size
= old_size
* 2;
813 assert(new_size
> old_size
);
814 assert(new_size
< IVAC_ENTRIES_MAX
);
816 new_table
= kalloc(sizeof(ivac_entry
) * new_size
);
818 panic("Failed to grow ivac table to size %d\n", new_size
);
822 /* setup the free list for new entries */
823 for (i
= old_size
; i
< new_size
; i
++) {
824 ivace_reset_data(&new_table
[i
], i
+ 1);
829 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
830 ivace_copy_data(&ivac
->ivac_table
[i
], &new_table
[i
]);
833 old_table
= ivac
->ivac_table
;
835 ivac
->ivac_table
= new_table
;
836 ivac
->ivac_table_size
= new_size
;
838 /* adding new free entries at head of freelist */
839 ivac
->ivac_table
[new_size
- 1].ivace_next
= ivac
->ivac_freelist
;
840 ivac
->ivac_freelist
= old_size
;
841 ivac
->ivac_is_growing
= 0;
846 kfree(old_table
, old_size
* sizeof(ivac_entry
));
852 * ivace_reference_by_index
854 * Take an additional reference on the <key_index, val_index>
855 * cached value. It is assumed the caller already holds a
856 * reference to the same cached key-value pair.
859 ivace_reference_by_index(
860 iv_index_t key_index
,
861 iv_index_t val_index
)
863 ipc_voucher_attr_control_t ivac
;
866 if (IV_UNUSED_VALINDEX
== val_index
) {
870 ivgt_lookup(key_index
, FALSE
, NULL
, &ivac
);
871 assert(IVAC_NULL
!= ivac
);
874 assert(val_index
< ivac
->ivac_table_size
);
875 ivace
= &ivac
->ivac_table
[val_index
];
877 assert(0xdeadc0dedeadc0de != ivace
->ivace_value
);
878 assert(0 < ivace
->ivace_refs
);
879 assert(!ivace
->ivace_free
);
881 /* Take ref only on non-persistent values */
882 if (!ivace
->ivace_persist
) {
890 * Look up the values for a given <key, index> pair.
892 * Consumes a reference on the passed voucher control.
893 * Either it is donated to a newly-created value cache
894 * or it is released (if we piggy back on an existing
895 * value cache entry).
898 ivace_reference_by_value(
899 ipc_voucher_attr_control_t ivac
,
900 mach_voucher_attr_value_handle_t value
,
901 mach_voucher_attr_value_flags_t flag
)
903 ivac_entry_t ivace
= IVACE_NULL
;
904 iv_index_t hash_index
;
907 if (IVAC_NULL
== ivac
) {
908 return IV_UNUSED_VALINDEX
;
913 hash_index
= IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
914 index
= ivac
->ivac_table
[hash_index
].ivace_index
;
915 while (index
!= IV_HASH_END
) {
916 assert(index
< ivac
->ivac_table_size
);
917 ivace
= &ivac
->ivac_table
[index
];
918 assert(!ivace
->ivace_free
);
920 if (ivace
->ivace_value
== value
) {
924 assert(ivace
->ivace_next
!= index
);
925 index
= ivace
->ivace_next
;
929 if (index
!= IV_HASH_END
) {
930 /* only add reference on non-persistent value */
931 if (!ivace
->ivace_persist
) {
941 /* insert new entry in the table */
942 index
= ivac
->ivac_freelist
;
943 if (IV_FREELIST_END
== index
) {
945 ivac_grow_table(ivac
);
949 /* take the entry off the freelist */
950 ivace
= &ivac
->ivac_table
[index
];
951 ivac
->ivac_freelist
= ivace
->ivace_next
;
953 /* initialize the new entry */
954 ivace
->ivace_value
= value
;
955 ivace
->ivace_refs
= 1;
956 ivace
->ivace_made
= 1;
957 ivace
->ivace_free
= FALSE
;
958 ivace
->ivace_persist
= (flag
& MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
) ? TRUE
: FALSE
;
960 /* insert the new entry in the proper hash chain */
961 ivace
->ivace_next
= ivac
->ivac_table
[hash_index
].ivace_index
;
962 ivac
->ivac_table
[hash_index
].ivace_index
= index
;
965 /* donated passed in ivac reference to new entry */
971 * Release a reference on the given <key_index, value_index> pair.
973 * Conditions: called with nothing locked, as it may cause
974 * callouts and/or messaging to the resource
979 iv_index_t key_index
,
980 iv_index_t value_index
)
982 ipc_voucher_attr_control_t ivac
;
983 ipc_voucher_attr_manager_t ivam
;
984 mach_voucher_attr_value_handle_t value
;
985 mach_voucher_attr_value_reference_t made
;
986 mach_voucher_attr_key_t key
;
987 iv_index_t hash_index
;
991 /* cant release the default value */
992 if (IV_UNUSED_VALINDEX
== value_index
) {
996 ivgt_lookup(key_index
, FALSE
, &ivam
, &ivac
);
997 assert(IVAC_NULL
!= ivac
);
998 assert(IVAM_NULL
!= ivam
);
1001 assert(value_index
< ivac
->ivac_table_size
);
1002 ivace
= &ivac
->ivac_table
[value_index
];
1004 assert(0 < ivace
->ivace_refs
);
1006 /* cant release persistent values */
1007 if (ivace
->ivace_persist
) {
1012 if (0 < --ivace
->ivace_refs
) {
1017 key
= iv_index_to_key(key_index
);
1018 assert(MACH_VOUCHER_ATTR_KEY_NONE
!= key
);
1021 * if last return reply is still pending,
1022 * let it handle this later return when
1023 * the previous reply comes in.
1025 if (ivace
->ivace_releasing
) {
1030 /* claim releasing */
1031 ivace
->ivace_releasing
= TRUE
;
1032 value
= ivace
->ivace_value
;
1035 assert(value
== ivace
->ivace_value
);
1036 assert(!ivace
->ivace_free
);
1037 made
= ivace
->ivace_made
;
1040 /* callout to manager's release_value */
1041 kr
= (ivam
->ivam_release_value
)(ivam
, key
, value
, made
);
1043 /* recalculate entry address as table may have changed */
1045 ivace
= &ivac
->ivac_table
[value_index
];
1046 assert(value
== ivace
->ivace_value
);
1049 * new made values raced with this return. If the
1050 * manager OK'ed the prior release, we have to start
1051 * the made numbering over again (pretend the race
1052 * didn't happen). If the entry has zero refs again,
1053 * re-drive the release.
1055 if (ivace
->ivace_made
!= made
) {
1056 if (KERN_SUCCESS
== kr
) {
1057 ivace
->ivace_made
-= made
;
1060 if (0 == ivace
->ivace_refs
) {
1064 ivace
->ivace_releasing
= FALSE
;
1069 * If the manager returned FAILURE, someone took a
1070 * reference on the value but have not updated the ivace,
1071 * release the lock and return since thread who got
1072 * the new reference will update the ivace and will have
1073 * non-zero reference on the value.
1075 if (KERN_SUCCESS
!= kr
) {
1076 ivace
->ivace_releasing
= FALSE
;
1082 assert(0 == ivace
->ivace_refs
);
1085 * going away - remove entry from its hash
1086 * If its at the head of the hash bucket list (common), unchain
1087 * at the head. Otherwise walk the chain until the next points
1088 * at this entry, and remove it from the the list there.
1090 hash_index
= iv_hash_value(key_index
, value
);
1091 if (ivac
->ivac_table
[hash_index
].ivace_index
== value_index
) {
1092 ivac
->ivac_table
[hash_index
].ivace_index
= ivace
->ivace_next
;
1094 hash_index
= ivac
->ivac_table
[hash_index
].ivace_index
;
1095 assert(IV_HASH_END
!= hash_index
);
1096 while (ivac
->ivac_table
[hash_index
].ivace_next
!= value_index
) {
1097 hash_index
= ivac
->ivac_table
[hash_index
].ivace_next
;
1098 assert(IV_HASH_END
!= hash_index
);
1100 ivac
->ivac_table
[hash_index
].ivace_next
= ivace
->ivace_next
;
1103 /* Put this entry on the freelist */
1104 ivace
->ivace_value
= 0xdeadc0dedeadc0de;
1105 ivace
->ivace_releasing
= FALSE
;
1106 ivace
->ivace_free
= TRUE
;
1107 ivace
->ivace_made
= 0;
1108 ivace
->ivace_next
= ivac
->ivac_freelist
;
1109 ivac
->ivac_freelist
= value_index
;
1112 /* release the reference this value held on its cache control */
1122 * Lookup an entry in the global table from the context of a manager
1123 * registration. Adds a reference to the control to keep the results
1124 * around (if needed).
1126 * Because of the calling point, we can't be sure the manager is
1127 * [fully] registered yet. So, we must hold the global table lock
1128 * during the lookup to synchronize with in-parallel registrations
1129 * (and possible table growth).
1132 ivgt_lookup(iv_index_t key_index
,
1133 boolean_t take_reference
,
1134 ipc_voucher_attr_manager_t
*manager
,
1135 ipc_voucher_attr_control_t
*control
)
1137 ipc_voucher_attr_control_t ivac
;
1139 if (key_index
< MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
) {
1141 if (NULL
!= manager
) {
1142 *manager
= iv_global_table
[key_index
].ivgte_manager
;
1144 ivac
= iv_global_table
[key_index
].ivgte_control
;
1145 if (IVAC_NULL
!= ivac
) {
1146 assert(key_index
== ivac
->ivac_key_index
);
1147 if (take_reference
) {
1148 assert(NULL
!= control
);
1149 ivac_reference(ivac
);
1153 if (NULL
!= control
) {
1157 if (NULL
!= manager
) {
1158 *manager
= IVAM_NULL
;
1160 if (NULL
!= control
) {
1161 *control
= IVAC_NULL
;
1167 * Routine: ipc_replace_voucher_value
1169 * Replace the <voucher, key> value with the results of
1170 * running the supplied command through the resource
1171 * manager's get-value callback.
1173 * Nothing locked (may invoke user-space repeatedly).
1174 * Caller holds references on voucher and previous voucher.
1176 static kern_return_t
1177 ipc_replace_voucher_value(
1178 ipc_voucher_t voucher
,
1179 mach_voucher_attr_key_t key
,
1180 mach_voucher_attr_recipe_command_t command
,
1181 ipc_voucher_t prev_voucher
,
1182 mach_voucher_attr_content_t content
,
1183 mach_voucher_attr_content_size_t content_size
)
1185 mach_voucher_attr_value_handle_t previous_vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1186 mach_voucher_attr_value_handle_array_size_t previous_vals_count
;
1187 mach_voucher_attr_value_handle_t new_value
;
1188 mach_voucher_attr_value_flags_t new_flag
;
1189 ipc_voucher_t new_value_voucher
;
1190 ipc_voucher_attr_manager_t ivam
;
1191 ipc_voucher_attr_control_t ivac
;
1192 iv_index_t prev_val_index
;
1193 iv_index_t save_val_index
;
1194 iv_index_t val_index
;
1195 iv_index_t key_index
;
1199 * Get the manager for this key_index.
1200 * Returns a reference on the control.
1202 key_index
= iv_key_to_index(key
);
1203 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1204 if (IVAM_NULL
== ivam
) {
1205 return KERN_INVALID_ARGUMENT
;
1208 /* save the current value stored in the forming voucher */
1209 save_val_index
= iv_lookup(voucher
, key_index
);
1212 * Get the previous value(s) for this key creation.
1213 * If a previous voucher is specified, they come from there.
1214 * Otherwise, they come from the intermediate values already
1215 * in the forming voucher.
1217 prev_val_index
= (IV_NULL
!= prev_voucher
) ?
1218 iv_lookup(prev_voucher
, key_index
) :
1220 ivace_lookup_values(key_index
, prev_val_index
,
1221 previous_vals
, &previous_vals_count
);
1223 /* Call out to resource manager to get new value */
1224 new_value_voucher
= IV_NULL
;
1225 kr
= (ivam
->ivam_get_value
)(
1227 previous_vals
, previous_vals_count
,
1228 content
, content_size
,
1229 &new_value
, &new_flag
, &new_value_voucher
);
1230 if (KERN_SUCCESS
!= kr
) {
1235 /* TODO: value insertion from returned voucher */
1236 if (IV_NULL
!= new_value_voucher
) {
1237 iv_release(new_value_voucher
);
1241 * Find or create a slot in the table associated
1242 * with this attribute value. The ivac reference
1243 * is transferred to a new value, or consumed if
1244 * we find a matching existing value.
1246 val_index
= ivace_reference_by_value(ivac
, new_value
, new_flag
);
1247 iv_set(voucher
, key_index
, val_index
);
1250 * release saved old value from the newly forming voucher
1251 * This is saved until the end to avoid churning the
1252 * release logic in cases where the same value is returned
1253 * as was there before.
1255 ivace_release(key_index
, save_val_index
);
1257 return KERN_SUCCESS
;
1261 * Routine: ipc_directly_replace_voucher_value
1263 * Replace the <voucher, key> value with the value-handle
1264 * supplied directly by the attribute manager.
1267 * Caller holds references on voucher.
1268 * A made reference to the value-handle is donated by the caller.
1270 static kern_return_t
1271 ipc_directly_replace_voucher_value(
1272 ipc_voucher_t voucher
,
1273 mach_voucher_attr_key_t key
,
1274 mach_voucher_attr_value_handle_t new_value
)
1276 ipc_voucher_attr_manager_t ivam
;
1277 ipc_voucher_attr_control_t ivac
;
1278 iv_index_t save_val_index
;
1279 iv_index_t val_index
;
1280 iv_index_t key_index
;
1283 * Get the manager for this key_index.
1284 * Returns a reference on the control.
1286 key_index
= iv_key_to_index(key
);
1287 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1288 if (IVAM_NULL
== ivam
) {
1289 return KERN_INVALID_ARGUMENT
;
1292 /* save the current value stored in the forming voucher */
1293 save_val_index
= iv_lookup(voucher
, key_index
);
1296 * Find or create a slot in the table associated
1297 * with this attribute value. The ivac reference
1298 * is transferred to a new value, or consumed if
1299 * we find a matching existing value.
1301 val_index
= ivace_reference_by_value(ivac
, new_value
,
1302 MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
);
1303 iv_set(voucher
, key_index
, val_index
);
1306 * release saved old value from the newly forming voucher
1307 * This is saved until the end to avoid churning the
1308 * release logic in cases where the same value is returned
1309 * as was there before.
1311 ivace_release(key_index
, save_val_index
);
1313 return KERN_SUCCESS
;
1316 static kern_return_t
1317 ipc_execute_voucher_recipe_command(
1318 ipc_voucher_t voucher
,
1319 mach_voucher_attr_key_t key
,
1320 mach_voucher_attr_recipe_command_t command
,
1321 ipc_voucher_t prev_iv
,
1322 mach_voucher_attr_content_t content
,
1323 mach_voucher_attr_content_size_t content_size
,
1326 iv_index_t prev_val_index
;
1327 iv_index_t val_index
;
1332 * MACH_VOUCHER_ATTR_COPY
1333 * Copy the attribute(s) from the previous voucher to the new
1334 * one. A wildcard key is an acceptable value - indicating a
1335 * desire to copy all the attribute values from the previous
1338 case MACH_VOUCHER_ATTR_COPY
:
1340 /* no recipe data on a copy */
1341 if (0 < content_size
) {
1342 return KERN_INVALID_ARGUMENT
;
1345 /* nothing to copy from? - done */
1346 if (IV_NULL
== prev_iv
) {
1347 return KERN_SUCCESS
;
1350 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1351 iv_index_t limit
, j
;
1353 /* reconcile possible difference in voucher sizes */
1354 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1355 prev_iv
->iv_table_size
:
1356 voucher
->iv_table_size
;
1358 /* wildcard matching */
1359 for (j
= 0; j
< limit
; j
++) {
1360 /* release old value being replaced */
1361 val_index
= iv_lookup(voucher
, j
);
1362 ivace_release(j
, val_index
);
1364 /* replace with reference to prev voucher's value */
1365 prev_val_index
= iv_lookup(prev_iv
, j
);
1366 ivace_reference_by_index(j
, prev_val_index
);
1367 iv_set(voucher
, j
, prev_val_index
);
1370 iv_index_t key_index
;
1372 /* copy just one key */
1373 key_index
= iv_key_to_index(key
);
1374 if (ivgt_keys_in_use
< key_index
) {
1375 return KERN_INVALID_ARGUMENT
;
1378 /* release old value being replaced */
1379 val_index
= iv_lookup(voucher
, key_index
);
1380 ivace_release(key_index
, val_index
);
1382 /* replace with reference to prev voucher's value */
1383 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1384 ivace_reference_by_index(key_index
, prev_val_index
);
1385 iv_set(voucher
, key_index
, prev_val_index
);
1390 * MACH_VOUCHER_ATTR_REMOVE
1391 * Remove the attribute(s) from the under construction voucher.
1392 * A wildcard key is an acceptable value - indicating a desire
1393 * to remove all the attribute values set up so far in the voucher.
1394 * If a previous voucher is specified, only remove the value it
1395 * it matches the value in the previous voucher.
1397 case MACH_VOUCHER_ATTR_REMOVE
:
1398 /* no recipe data on a remove */
1399 if (0 < content_size
) {
1400 return KERN_INVALID_ARGUMENT
;
1403 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1404 iv_index_t limit
, j
;
1406 /* reconcile possible difference in voucher sizes */
1407 limit
= (IV_NULL
== prev_iv
) ? voucher
->iv_table_size
:
1408 ((prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1409 prev_iv
->iv_table_size
: voucher
->iv_table_size
);
1411 /* wildcard matching */
1412 for (j
= 0; j
< limit
; j
++) {
1413 val_index
= iv_lookup(voucher
, j
);
1415 /* If not matched in previous, skip */
1416 if (IV_NULL
!= prev_iv
) {
1417 prev_val_index
= iv_lookup(prev_iv
, j
);
1418 if (val_index
!= prev_val_index
) {
1422 /* release and clear */
1423 ivace_release(j
, val_index
);
1424 iv_set(voucher
, j
, IV_UNUSED_VALINDEX
);
1427 iv_index_t key_index
;
1429 /* copy just one key */
1430 key_index
= iv_key_to_index(key
);
1431 if (ivgt_keys_in_use
< key_index
) {
1432 return KERN_INVALID_ARGUMENT
;
1435 val_index
= iv_lookup(voucher
, key_index
);
1437 /* If not matched in previous, skip */
1438 if (IV_NULL
!= prev_iv
) {
1439 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1440 if (val_index
!= prev_val_index
) {
1445 /* release and clear */
1446 ivace_release(key_index
, val_index
);
1447 iv_set(voucher
, key_index
, IV_UNUSED_VALINDEX
);
1452 * MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
1453 * Use key-privilege to set a value handle for the attribute directly,
1454 * rather than triggering a callback into the attribute manager to
1455 * interpret a recipe to generate the value handle.
1457 case MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
:
1459 mach_voucher_attr_value_handle_t new_value
;
1461 if (sizeof(mach_voucher_attr_value_handle_t
) != content_size
) {
1462 return KERN_INVALID_ARGUMENT
;
1465 new_value
= *(mach_voucher_attr_value_handle_t
*)(void *)content
;
1466 kr
= ipc_directly_replace_voucher_value(voucher
,
1468 if (KERN_SUCCESS
!= kr
) {
1472 return KERN_INVALID_CAPABILITY
;
1477 * MACH_VOUCHER_ATTR_REDEEM
1478 * Redeem the attribute(s) from the previous voucher for a possibly
1479 * new value in the new voucher. A wildcard key is an acceptable value,
1480 * indicating a desire to redeem all the values.
1482 case MACH_VOUCHER_ATTR_REDEEM
:
1484 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1485 iv_index_t limit
, j
;
1487 /* reconcile possible difference in voucher sizes */
1488 if (IV_NULL
!= prev_iv
) {
1489 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1490 prev_iv
->iv_table_size
:
1491 voucher
->iv_table_size
;
1493 limit
= voucher
->iv_table_size
;
1496 /* wildcard matching */
1497 for (j
= 0; j
< limit
; j
++) {
1498 mach_voucher_attr_key_t j_key
;
1500 j_key
= iv_index_to_key(j
);
1502 /* skip non-existent managers */
1503 if (MACH_VOUCHER_ATTR_KEY_NONE
== j_key
) {
1507 /* get the new value from redeem (skip empty previous) */
1508 kr
= ipc_replace_voucher_value(voucher
,
1514 if (KERN_SUCCESS
!= kr
) {
1520 /* fall thru for single key redemption */
1524 * Replace the current value for the <voucher, key> pair with whatever
1525 * value the resource manager returns for the command and recipe
1526 * combination provided.
1529 kr
= ipc_replace_voucher_value(voucher
,
1535 if (KERN_SUCCESS
!= kr
) {
1541 return KERN_SUCCESS
;
1545 * Routine: iv_checksum
1547 * Compute the voucher sum. This is more position-
1548 * relevant than many other checksums - important for
1549 * vouchers (arrays of low, oft-reused, indexes).
1551 static inline iv_index_t
1552 iv_checksum(ipc_voucher_t voucher
, boolean_t
*emptyp
)
1556 boolean_t empty
= TRUE
;
1557 if (0 < voucher
->iv_table_size
) {
1558 iv_index_t i
= voucher
->iv_table_size
- 1;
1561 iv_index_t v
= voucher
->iv_table
[i
];
1562 c
= c
<< 3 | c
>> (32 - 3); /* rotate */
1563 c
= ~c
; /* invert */
1565 c
+= v
; /* add in */
1577 * See if the set of values represented by this new voucher
1578 * already exist in another voucher. If so return a reference
1579 * to the existing voucher and deallocate the voucher provided.
1580 * Otherwise, insert this one in the hash and return it.
1582 * A voucher reference is donated on entry.
1584 * A voucher reference (may be different than on entry).
1586 static ipc_voucher_t
1587 iv_dedup(ipc_voucher_t new_iv
)
1594 sum
= iv_checksum(new_iv
, &empty
);
1596 /* If all values are default, that's the empty (NULL) voucher */
1598 iv_dealloc(new_iv
, FALSE
);
1602 hash
= IV_HASH_BUCKET(sum
);
1605 queue_iterate(&ivht_bucket
[hash
], iv
, ipc_voucher_t
, iv_hash_link
) {
1606 assert(iv
->iv_hash
== hash
);
1608 /* if not already deallocating and sums match... */
1609 if ((os_ref_get_count(&iv
->iv_refs
) > 0) && (iv
->iv_sum
== sum
)) {
1612 assert(iv
->iv_table_size
<= new_iv
->iv_table_size
);
1614 /* and common entries match... */
1615 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
1616 if (iv
->iv_table
[i
] != new_iv
->iv_table
[i
]) {
1620 if (i
< iv
->iv_table_size
) {
1624 /* and all extra entries in new one are unused... */
1625 while (i
< new_iv
->iv_table_size
) {
1626 if (new_iv
->iv_table
[i
++] != IV_UNUSED_VALINDEX
) {
1630 if (i
< new_iv
->iv_table_size
) {
1634 /* ... we found a match... */
1636 /* can we get a ref before it hits 0
1638 * This is thread safe. If the reference count is zero before we
1639 * adjust it, no other thread can have a reference to the voucher.
1640 * The dealloc code requires holding the ivht_lock, so
1641 * the voucher cannot be yanked out from under us.
1643 if (!os_ref_retain_try(&iv
->iv_refs
)) {
1649 /* referenced previous, so deallocate the new one */
1650 iv_dealloc(new_iv
, FALSE
);
1655 /* add the new voucher to the hash, and return it */
1656 new_iv
->iv_sum
= sum
;
1657 new_iv
->iv_hash
= hash
;
1658 queue_enter(&ivht_bucket
[hash
], new_iv
, ipc_voucher_t
, iv_hash_link
);
1663 * This code is disabled for KDEBUG_LEVEL_IST and KDEBUG_LEVEL_NONE
1665 #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD)
1666 if (kdebug_enable
& ~KDEBUG_ENABLE_PPT
) {
1667 uintptr_t voucher_addr
= VM_KERNEL_ADDRPERM((uintptr_t)new_iv
);
1668 uintptr_t attr_tracepoints_needed
= 0;
1670 if (ipc_voucher_trace_contents
) {
1672 * voucher_contents sizing is a bit more constrained
1673 * than might be obvious.
1675 * This is typically a uint8_t typed array. However,
1676 * we want to access it as a uintptr_t to efficiently
1677 * copyout the data in tracepoints.
1679 * This constrains the size to uintptr_t bytes, and
1680 * adds a minimimum alignment requirement equivalent
1683 * Further constraining the size is the fact that it
1684 * is copied out 4 uintptr_t chunks at a time. We do
1685 * NOT want to run off the end of the array and copyout
1686 * random stack data.
1688 * So the minimum size is 4 * sizeof(uintptr_t), and
1689 * the minimum alignment is uintptr_t aligned.
1692 #define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
1693 #define PAYLOAD_SIZE 1024
1695 static_assert(PAYLOAD_SIZE
% PAYLOAD_PER_TRACEPOINT
== 0, "size invariant violated");
1697 mach_voucher_attr_raw_recipe_array_size_t payload_size
= PAYLOAD_SIZE
;
1698 uintptr_t payload
[PAYLOAD_SIZE
/ sizeof(uintptr_t)];
1701 kr
= mach_voucher_extract_all_attr_recipes(new_iv
, (mach_voucher_attr_raw_recipe_array_t
)payload
, &payload_size
);
1702 if (KERN_SUCCESS
== kr
) {
1703 attr_tracepoints_needed
= (payload_size
+ PAYLOAD_PER_TRACEPOINT
- 1) / PAYLOAD_PER_TRACEPOINT
;
1706 * To prevent leaking data from the stack, we
1707 * need to zero data to the end of a tracepoint
1710 size_t remainder
= payload_size
% PAYLOAD_PER_TRACEPOINT
;
1712 bzero((uint8_t*)payload
+ payload_size
,
1713 PAYLOAD_PER_TRACEPOINT
- remainder
);
1717 KDBG(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_CREATE
),
1718 voucher_addr
, new_iv
->iv_table_size
, ivht_count
,
1721 uintptr_t index
= 0;
1722 while (attr_tracepoints_needed
--) {
1723 KDBG(MACHDBG_CODE(DBG_MACH_IPC
,
1724 MACH_IPC_VOUCHER_CREATE_ATTR_DATA
), payload
[index
],
1725 payload
[index
+ 1], payload
[index
+ 2],
1726 payload
[index
+ 3]);
1730 KDBG(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_CREATE
),
1731 voucher_addr
, new_iv
->iv_table_size
, ivht_count
);
1734 #endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
1740 * Routine: ipc_create_mach_voucher
1742 * Create a new mach voucher and initialize it with the
1743 * value(s) created by having the appropriate resource
1744 * managers interpret the supplied recipe commands and
1747 * Nothing locked (may invoke user-space repeatedly).
1748 * Caller holds references on previous vouchers.
1749 * Previous vouchers are passed as voucher indexes.
1752 ipc_create_mach_voucher(
1753 ipc_voucher_attr_raw_recipe_array_t recipes
,
1754 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1755 ipc_voucher_t
*new_voucher
)
1757 ipc_voucher_attr_recipe_t sub_recipe
;
1758 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1759 ipc_voucher_t voucher
;
1760 kern_return_t kr
= KERN_SUCCESS
;
1762 /* if nothing to do ... */
1763 if (0 == recipe_size
) {
1764 *new_voucher
= IV_NULL
;
1765 return KERN_SUCCESS
;
1768 /* allocate a voucher */
1769 voucher
= iv_alloc(ivgt_keys_in_use
);
1770 if (IV_NULL
== voucher
) {
1771 return KERN_RESOURCE_SHORTAGE
;
1774 /* iterate over the recipe items */
1775 while (0 < recipe_size
- recipe_used
) {
1776 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1777 kr
= KERN_INVALID_ARGUMENT
;
1781 /* find the next recipe */
1782 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1783 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1784 kr
= KERN_INVALID_ARGUMENT
;
1787 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1789 kr
= ipc_execute_voucher_recipe_command(voucher
,
1791 sub_recipe
->command
,
1792 sub_recipe
->previous_voucher
,
1793 sub_recipe
->content
,
1794 sub_recipe
->content_size
,
1796 if (KERN_SUCCESS
!= kr
) {
1801 if (KERN_SUCCESS
== kr
) {
1802 *new_voucher
= iv_dedup(voucher
);
1804 iv_dealloc(voucher
, FALSE
);
1805 *new_voucher
= IV_NULL
;
1811 * Routine: ipc_voucher_attr_control_create_mach_voucher
1813 * Create a new mach voucher and initialize it with the
1814 * value(s) created by having the appropriate resource
1815 * managers interpret the supplied recipe commands and
1818 * The resource manager control's privilege over its
1819 * particular key value is reflected on to the execution
1820 * code, allowing internal commands (like setting a
1821 * key value handle directly, rather than having to
1822 * create a recipe, that will generate a callback just
1826 * Nothing locked (may invoke user-space repeatedly).
1827 * Caller holds references on previous vouchers.
1828 * Previous vouchers are passed as voucher indexes.
1831 ipc_voucher_attr_control_create_mach_voucher(
1832 ipc_voucher_attr_control_t control
,
1833 ipc_voucher_attr_raw_recipe_array_t recipes
,
1834 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1835 ipc_voucher_t
*new_voucher
)
1837 mach_voucher_attr_key_t control_key
;
1838 ipc_voucher_attr_recipe_t sub_recipe
;
1839 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1840 ipc_voucher_t voucher
= IV_NULL
;
1841 kern_return_t kr
= KERN_SUCCESS
;
1843 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
1844 return KERN_INVALID_CAPABILITY
;
1847 /* if nothing to do ... */
1848 if (0 == recipe_size
) {
1849 *new_voucher
= IV_NULL
;
1850 return KERN_SUCCESS
;
1853 /* allocate new voucher */
1854 voucher
= iv_alloc(ivgt_keys_in_use
);
1855 if (IV_NULL
== voucher
) {
1856 return KERN_RESOURCE_SHORTAGE
;
1859 control_key
= iv_index_to_key(control
->ivac_key_index
);
1861 /* iterate over the recipe items */
1862 while (0 < recipe_size
- recipe_used
) {
1863 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1864 kr
= KERN_INVALID_ARGUMENT
;
1868 /* find the next recipe */
1869 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1870 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1871 kr
= KERN_INVALID_ARGUMENT
;
1874 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1876 kr
= ipc_execute_voucher_recipe_command(voucher
,
1878 sub_recipe
->command
,
1879 sub_recipe
->previous_voucher
,
1880 sub_recipe
->content
,
1881 sub_recipe
->content_size
,
1882 (sub_recipe
->key
== control_key
));
1883 if (KERN_SUCCESS
!= kr
) {
1888 if (KERN_SUCCESS
== kr
) {
1889 *new_voucher
= iv_dedup(voucher
);
1891 *new_voucher
= IV_NULL
;
1892 iv_dealloc(voucher
, FALSE
);
1898 * ipc_register_well_known_mach_voucher_attr_manager
1900 * Register the resource manager responsible for a given key value.
1903 ipc_register_well_known_mach_voucher_attr_manager(
1904 ipc_voucher_attr_manager_t manager
,
1905 mach_voucher_attr_value_handle_t default_value
,
1906 mach_voucher_attr_key_t key
,
1907 ipc_voucher_attr_control_t
*control
)
1909 ipc_voucher_attr_control_t new_control
;
1910 iv_index_t key_index
;
1911 iv_index_t hash_index
;
1913 if (IVAM_NULL
== manager
) {
1914 return KERN_INVALID_ARGUMENT
;
1917 key_index
= iv_key_to_index(key
);
1918 if (IV_UNUSED_KEYINDEX
== key_index
) {
1919 return KERN_INVALID_ARGUMENT
;
1922 new_control
= ivac_alloc(key_index
);
1923 if (IVAC_NULL
== new_control
) {
1924 return KERN_RESOURCE_SHORTAGE
;
1927 /* insert the default value into slot 0 */
1928 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_value
= default_value
;
1929 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_refs
= IVACE_REFS_MAX
;
1930 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_made
= IVACE_REFS_MAX
;
1931 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_persist
= TRUE
;
1932 assert(IV_HASH_END
== new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_next
);
1935 if (IVAM_NULL
!= iv_global_table
[key_index
].ivgte_manager
) {
1937 ivac_release(new_control
);
1938 return KERN_INVALID_ARGUMENT
;
1941 /* fill in the global table slot for this key */
1942 iv_global_table
[key_index
].ivgte_manager
= manager
;
1943 iv_global_table
[key_index
].ivgte_control
= new_control
;
1944 iv_global_table
[key_index
].ivgte_key
= key
;
1946 /* insert the default value into the hash (in case it is returned later) */
1947 hash_index
= iv_hash_value(key_index
, default_value
);
1948 assert(IV_HASH_END
== new_control
->ivac_table
[hash_index
].ivace_index
);
1949 new_control
->ivac_table
[hash_index
].ivace_index
= IV_UNUSED_VALINDEX
;
1953 /* return the reference on the new cache control to the caller */
1954 *control
= new_control
;
1956 return KERN_SUCCESS
;
1960 * Routine: mach_voucher_extract_attr_content
1962 * Extract the content for a given <voucher, key> pair.
1964 * If a value other than the default is present for this
1965 * <voucher,key> pair, we need to contact the resource
1966 * manager to extract the content/meaning of the value(s)
1967 * present. Otherwise, return success (but no data).
1970 * Nothing locked - as it may upcall to user-space.
1971 * The caller holds a reference on the voucher.
1974 mach_voucher_extract_attr_content(
1975 ipc_voucher_t voucher
,
1976 mach_voucher_attr_key_t key
,
1977 mach_voucher_attr_content_t content
,
1978 mach_voucher_attr_content_size_t
*in_out_size
)
1980 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1981 mach_voucher_attr_value_handle_array_size_t vals_count
;
1982 mach_voucher_attr_recipe_command_t command
;
1983 ipc_voucher_attr_manager_t manager
;
1984 iv_index_t value_index
;
1985 iv_index_t key_index
;
1989 if (IV_NULL
== voucher
) {
1990 return KERN_INVALID_ARGUMENT
;
1993 key_index
= iv_key_to_index(key
);
1995 value_index
= iv_lookup(voucher
, key_index
);
1996 if (IV_UNUSED_VALINDEX
== value_index
) {
1998 return KERN_SUCCESS
;
2002 * Get the manager for this key_index. The
2003 * existence of a non-default value for this
2004 * slot within our voucher will keep the
2005 * manager referenced during the callout.
2007 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2008 if (IVAM_NULL
== manager
) {
2009 return KERN_INVALID_ARGUMENT
;
2013 * Get the value(s) to pass to the manager
2014 * for this value_index.
2016 ivace_lookup_values(key_index
, value_index
,
2018 assert(0 < vals_count
);
2020 /* callout to manager */
2022 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2023 vals
, vals_count
, &command
, content
, in_out_size
);
2028 * Routine: mach_voucher_extract_attr_recipe
2030 * Extract a recipe for a given <voucher, key> pair.
2032 * If a value other than the default is present for this
2033 * <voucher,key> pair, we need to contact the resource
2034 * manager to extract the content/meaning of the value(s)
2035 * present. Otherwise, return success (but no data).
2038 * Nothing locked - as it may upcall to user-space.
2039 * The caller holds a reference on the voucher.
2042 mach_voucher_extract_attr_recipe(
2043 ipc_voucher_t voucher
,
2044 mach_voucher_attr_key_t key
,
2045 mach_voucher_attr_raw_recipe_t raw_recipe
,
2046 mach_voucher_attr_raw_recipe_size_t
*in_out_size
)
2048 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2049 mach_voucher_attr_value_handle_array_size_t vals_count
;
2050 ipc_voucher_attr_manager_t manager
;
2051 mach_voucher_attr_recipe_t recipe
;
2052 iv_index_t value_index
;
2053 iv_index_t key_index
;
2057 if (IV_NULL
== voucher
) {
2058 return KERN_INVALID_ARGUMENT
;
2061 key_index
= iv_key_to_index(key
);
2063 value_index
= iv_lookup(voucher
, key_index
);
2064 if (IV_UNUSED_VALINDEX
== value_index
) {
2066 return KERN_SUCCESS
;
2069 if (*in_out_size
< sizeof(*recipe
)) {
2070 return KERN_NO_SPACE
;
2073 recipe
= (mach_voucher_attr_recipe_t
)(void *)raw_recipe
;
2075 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2076 recipe
->previous_voucher
= MACH_VOUCHER_NAME_NULL
;
2077 recipe
->content_size
= *in_out_size
- sizeof(*recipe
);
2080 * Get the manager for this key_index. The
2081 * existence of a non-default value for this
2082 * slot within our voucher will keep the
2083 * manager referenced during the callout.
2085 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2086 if (IVAM_NULL
== manager
) {
2087 return KERN_INVALID_ARGUMENT
;
2091 * Get the value(s) to pass to the manager
2092 * for this value_index.
2094 ivace_lookup_values(key_index
, value_index
,
2096 assert(0 < vals_count
);
2098 /* callout to manager */
2099 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2102 recipe
->content
, &recipe
->content_size
);
2103 if (KERN_SUCCESS
== kr
) {
2104 assert(*in_out_size
- sizeof(*recipe
) >= recipe
->content_size
);
2105 *in_out_size
= sizeof(*recipe
) + recipe
->content_size
;
2114 * Routine: mach_voucher_extract_all_attr_recipes
2116 * Extract all the (non-default) contents for a given voucher,
2117 * building up a recipe that could be provided to a future
2118 * voucher creation call.
2120 * Nothing locked (may invoke user-space).
2121 * Caller holds a reference on the supplied voucher.
2124 mach_voucher_extract_all_attr_recipes(
2125 ipc_voucher_t voucher
,
2126 mach_voucher_attr_raw_recipe_array_t recipes
,
2127 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2129 mach_voucher_attr_recipe_size_t recipe_size
= *in_out_size
;
2130 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2131 iv_index_t key_index
;
2133 if (IV_NULL
== voucher
) {
2134 return KERN_INVALID_ARGUMENT
;
2137 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2138 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2139 mach_voucher_attr_value_handle_array_size_t vals_count
;
2140 mach_voucher_attr_content_size_t content_size
;
2141 ipc_voucher_attr_manager_t manager
;
2142 mach_voucher_attr_recipe_t recipe
;
2143 mach_voucher_attr_key_t key
;
2144 iv_index_t value_index
;
2147 /* don't output anything for a default value */
2148 value_index
= iv_lookup(voucher
, key_index
);
2149 if (IV_UNUSED_VALINDEX
== value_index
) {
2153 if (recipe_size
- recipe_used
< sizeof(*recipe
)) {
2154 return KERN_NO_SPACE
;
2158 * Get the manager for this key_index. The
2159 * existence of a non-default value for this
2160 * slot within our voucher will keep the
2161 * manager referenced during the callout.
2163 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2164 assert(IVAM_NULL
!= manager
);
2165 if (IVAM_NULL
== manager
) {
2169 recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2170 content_size
= recipe_size
- recipe_used
- sizeof(*recipe
);
2173 * Get the value(s) to pass to the manager
2174 * for this value_index.
2176 ivace_lookup_values(key_index
, value_index
,
2178 assert(0 < vals_count
);
2180 key
= iv_index_to_key(key_index
);
2183 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2184 recipe
->content_size
= content_size
;
2186 /* callout to manager */
2187 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2190 recipe
->content
, &recipe
->content_size
);
2191 if (KERN_SUCCESS
!= kr
) {
2195 assert(recipe
->content_size
<= content_size
);
2196 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2199 *in_out_size
= recipe_used
;
2200 return KERN_SUCCESS
;
2204 * Routine: mach_voucher_debug_info
2206 * Extract all the (non-default) contents for a given mach port name,
2207 * building up a recipe that could be provided to a future
2208 * voucher creation call.
2210 * Nothing locked (may invoke user-space).
2211 * Caller may not hold a reference on the supplied voucher.
2213 #if !(DEVELOPMENT || DEBUG)
2215 mach_voucher_debug_info(
2216 ipc_space_t __unused space
,
2217 mach_port_name_t __unused voucher_name
,
2218 mach_voucher_attr_raw_recipe_array_t __unused recipes
,
2219 mach_voucher_attr_raw_recipe_array_size_t __unused
*in_out_size
)
2221 return KERN_NOT_SUPPORTED
;
2225 mach_voucher_debug_info(
2227 mach_port_name_t voucher_name
,
2228 mach_voucher_attr_raw_recipe_array_t recipes
,
2229 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2231 ipc_voucher_t voucher
= IPC_VOUCHER_NULL
;
2233 ipc_port_t port
= MACH_PORT_NULL
;
2235 if (space
== IS_NULL
) {
2236 return KERN_INVALID_TASK
;
2239 if (!MACH_PORT_VALID(voucher_name
)) {
2240 return KERN_INVALID_ARGUMENT
;
2243 kr
= ipc_port_translate_send(space
, voucher_name
, &port
);
2244 if (KERN_SUCCESS
!= kr
) {
2245 return KERN_INVALID_ARGUMENT
;
2248 voucher
= convert_port_to_voucher(port
);
2252 kr
= mach_voucher_extract_all_attr_recipes(voucher
, recipes
, in_out_size
);
2253 ipc_voucher_release(voucher
);
2257 return KERN_FAILURE
;
2262 * Routine: mach_voucher_attr_command
2264 * Invoke an attribute-specific command through this voucher.
2266 * The voucher layout, membership, etc... is not altered
2267 * through the execution of this command.
2270 * Nothing locked - as it may upcall to user-space.
2271 * The caller holds a reference on the voucher.
2274 mach_voucher_attr_command(
2275 ipc_voucher_t voucher
,
2276 mach_voucher_attr_key_t key
,
2277 mach_voucher_attr_command_t command
,
2278 mach_voucher_attr_content_t in_content
,
2279 mach_voucher_attr_content_size_t in_content_size
,
2280 mach_voucher_attr_content_t out_content
,
2281 mach_voucher_attr_content_size_t
*out_content_size
)
2283 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2284 mach_voucher_attr_value_handle_array_size_t vals_count
;
2285 ipc_voucher_attr_manager_t manager
;
2286 ipc_voucher_attr_control_t control
;
2287 iv_index_t value_index
;
2288 iv_index_t key_index
;
2292 if (IV_NULL
== voucher
) {
2293 return KERN_INVALID_ARGUMENT
;
2296 key_index
= iv_key_to_index(key
);
2299 * Get the manager for this key_index.
2300 * Allowing commands against the default value
2301 * for an attribute means that we have to hold
2302 * reference on the attribute manager control
2303 * to keep the manager around during the command
2306 ivgt_lookup(key_index
, TRUE
, &manager
, &control
);
2307 if (IVAM_NULL
== manager
) {
2308 return KERN_INVALID_ARGUMENT
;
2312 * Get the values for this <voucher, key> pair
2313 * to pass to the attribute manager. It is still
2314 * permissible to execute a command against the
2315 * default value (empty value array).
2317 value_index
= iv_lookup(voucher
, key_index
);
2318 ivace_lookup_values(key_index
, value_index
,
2321 /* callout to manager */
2322 kr
= (manager
->ivam_command
)(manager
, key
,
2325 in_content
, in_content_size
,
2326 out_content
, out_content_size
);
2328 /* release reference on control */
2329 ivac_release(control
);
2335 * Routine: mach_voucher_attr_control_get_values
2337 * For a given voucher, get the value handle associated with the
2338 * specified attribute manager.
2341 mach_voucher_attr_control_get_values(
2342 ipc_voucher_attr_control_t control
,
2343 ipc_voucher_t voucher
,
2344 mach_voucher_attr_value_handle_array_t out_values
,
2345 mach_voucher_attr_value_handle_array_size_t
*in_out_size
)
2347 iv_index_t key_index
, value_index
;
2349 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
2350 return KERN_INVALID_CAPABILITY
;
2353 if (IV_NULL
== voucher
) {
2354 return KERN_INVALID_ARGUMENT
;
2357 if (0 == *in_out_size
) {
2358 return KERN_SUCCESS
;
2361 key_index
= control
->ivac_key_index
;
2363 assert(os_ref_get_count(&voucher
->iv_refs
) > 0);
2364 value_index
= iv_lookup(voucher
, key_index
);
2365 ivace_lookup_values(key_index
, value_index
,
2366 out_values
, in_out_size
);
2367 return KERN_SUCCESS
;
2371 * Routine: mach_voucher_attr_control_create_mach_voucher
2373 * Create a new mach voucher and initialize it by processing the
2374 * supplied recipe(s).
2376 * Coming in on the attribute control port denotes special privileges
2377 * over they key associated with the control port.
2379 * Coming in from user-space, each recipe item will have a previous
2380 * recipe port name that needs to be converted to a voucher. Because
2381 * we can't rely on the port namespace to hold a reference on each
2382 * previous voucher port for the duration of processing that command,
2383 * we have to convert the name to a voucher reference and release it
2384 * after the command processing is done.
2387 mach_voucher_attr_control_create_mach_voucher(
2388 ipc_voucher_attr_control_t control
,
2389 mach_voucher_attr_raw_recipe_array_t recipes
,
2390 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2391 ipc_voucher_t
*new_voucher
)
2393 mach_voucher_attr_key_t control_key
;
2394 mach_voucher_attr_recipe_t sub_recipe
;
2395 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2396 ipc_voucher_t voucher
= IV_NULL
;
2397 kern_return_t kr
= KERN_SUCCESS
;
2399 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
2400 return KERN_INVALID_CAPABILITY
;
2403 /* if nothing to do ... */
2404 if (0 == recipe_size
) {
2405 *new_voucher
= IV_NULL
;
2406 return KERN_SUCCESS
;
2409 /* allocate new voucher */
2410 voucher
= iv_alloc(ivgt_keys_in_use
);
2411 if (IV_NULL
== voucher
) {
2412 return KERN_RESOURCE_SHORTAGE
;
2415 control_key
= iv_index_to_key(control
->ivac_key_index
);
2417 /* iterate over the recipe items */
2418 while (0 < recipe_size
- recipe_used
) {
2419 ipc_voucher_t prev_iv
;
2421 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2422 kr
= KERN_INVALID_ARGUMENT
;
2426 /* find the next recipe */
2427 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2428 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2429 kr
= KERN_INVALID_ARGUMENT
;
2432 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2434 /* convert voucher port name (current space) into a voucher reference */
2435 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2436 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2437 kr
= KERN_INVALID_CAPABILITY
;
2441 kr
= ipc_execute_voucher_recipe_command(voucher
,
2443 sub_recipe
->command
,
2445 sub_recipe
->content
,
2446 sub_recipe
->content_size
,
2447 (sub_recipe
->key
== control_key
));
2448 ipc_voucher_release(prev_iv
);
2450 if (KERN_SUCCESS
!= kr
) {
2455 if (KERN_SUCCESS
== kr
) {
2456 *new_voucher
= iv_dedup(voucher
);
2458 *new_voucher
= IV_NULL
;
2459 iv_dealloc(voucher
, FALSE
);
2465 * Routine: host_create_mach_voucher
2467 * Create a new mach voucher and initialize it by processing the
2468 * supplied recipe(s).
2470 * Comming in from user-space, each recipe item will have a previous
2471 * recipe port name that needs to be converted to a voucher. Because
2472 * we can't rely on the port namespace to hold a reference on each
2473 * previous voucher port for the duration of processing that command,
2474 * we have to convert the name to a voucher reference and release it
2475 * after the command processing is done.
2478 host_create_mach_voucher(
2480 mach_voucher_attr_raw_recipe_array_t recipes
,
2481 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2482 ipc_voucher_t
*new_voucher
)
2484 mach_voucher_attr_recipe_t sub_recipe
;
2485 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2486 ipc_voucher_t voucher
= IV_NULL
;
2487 kern_return_t kr
= KERN_SUCCESS
;
2489 if (host
== HOST_NULL
) {
2490 return KERN_INVALID_ARGUMENT
;
2493 /* if nothing to do ... */
2494 if (0 == recipe_size
) {
2495 *new_voucher
= IV_NULL
;
2496 return KERN_SUCCESS
;
2499 /* allocate new voucher */
2500 voucher
= iv_alloc(ivgt_keys_in_use
);
2501 if (IV_NULL
== voucher
) {
2502 return KERN_RESOURCE_SHORTAGE
;
2505 /* iterate over the recipe items */
2506 while (0 < recipe_size
- recipe_used
) {
2507 ipc_voucher_t prev_iv
;
2509 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2510 kr
= KERN_INVALID_ARGUMENT
;
2514 /* find the next recipe */
2515 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2516 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2517 kr
= KERN_INVALID_ARGUMENT
;
2520 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2522 /* convert voucher port name (current space) into a voucher reference */
2523 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2524 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2525 kr
= KERN_INVALID_CAPABILITY
;
2529 kr
= ipc_execute_voucher_recipe_command(voucher
,
2531 sub_recipe
->command
,
2533 sub_recipe
->content
,
2534 sub_recipe
->content_size
,
2536 ipc_voucher_release(prev_iv
);
2538 if (KERN_SUCCESS
!= kr
) {
2543 if (KERN_SUCCESS
== kr
) {
2544 *new_voucher
= iv_dedup(voucher
);
2546 *new_voucher
= IV_NULL
;
2547 iv_dealloc(voucher
, FALSE
);
2553 * Routine: host_register_well_known_mach_voucher_attr_manager
2555 * Register the user-level resource manager responsible for a given
2558 * The manager port passed in has to be converted/wrapped
2559 * in an ipc_voucher_attr_manager_t structure and then call the
2560 * internal variant. We have a generic ipc voucher manager
2561 * type that implements a MIG proxy out to user-space just for
2565 host_register_well_known_mach_voucher_attr_manager(
2567 mach_voucher_attr_manager_t __unused manager
,
2568 mach_voucher_attr_value_handle_t __unused default_value
,
2569 mach_voucher_attr_key_t __unused key
,
2570 ipc_voucher_attr_control_t __unused
*control
)
2572 if (HOST_NULL
== host
) {
2573 return KERN_INVALID_HOST
;
2577 return KERN_NOT_SUPPORTED
;
2580 * Allocate a mig_voucher_attr_manager_t that provides the
2581 * MIG proxy functions for the three manager callbacks and
2582 * store the port right in there.
2584 * If the user-space manager dies, we'll detect it on our
2585 * next upcall, and cleanup the proxy at that point.
2587 mig_voucher_attr_manager_t proxy
;
2590 proxy
= mvam_alloc(manager
);
2592 kr
= ipc_register_well_known_mach_voucher_attr_manager(&proxy
->mvam_manager
,
2596 if (KERN_SUCCESS
!= kr
) {
2597 mvam_release(proxy
);
2605 * Routine: host_register_mach_voucher_attr_manager
2607 * Register the user-space resource manager and return a
2608 * dynamically allocated key.
2610 * Wrap the supplied port with the MIG proxy ipc
2611 * voucher resource manager, and then call the internal
2615 host_register_mach_voucher_attr_manager(
2617 mach_voucher_attr_manager_t __unused manager
,
2618 mach_voucher_attr_value_handle_t __unused default_value
,
2619 mach_voucher_attr_key_t __unused
*key
,
2620 ipc_voucher_attr_control_t __unused
*control
)
2622 if (HOST_NULL
== host
) {
2623 return KERN_INVALID_HOST
;
2626 return KERN_NOT_SUPPORTED
;
2630 * Routine: ipc_get_pthpriority_from_kmsg_voucher
2632 * Get the canonicalized pthread priority from the voucher attached in the kmsg.
2635 ipc_get_pthpriority_from_kmsg_voucher(
2637 ipc_pthread_priority_value_t
*canonicalize_priority_value
)
2639 ipc_voucher_t pthread_priority_voucher
;
2640 mach_voucher_attr_raw_recipe_size_t content_size
=
2641 sizeof(mach_voucher_attr_recipe_data_t
) + sizeof(ipc_pthread_priority_value_t
);
2642 uint8_t content_data
[content_size
];
2643 mach_voucher_attr_recipe_t cur_content
;
2644 kern_return_t kr
= KERN_SUCCESS
;
2646 if (!IP_VALID(kmsg
->ikm_voucher
)) {
2647 return KERN_FAILURE
;
2650 pthread_priority_voucher
= (ipc_voucher_t
)ip_get_kobject(kmsg
->ikm_voucher
);
2651 kr
= mach_voucher_extract_attr_recipe(pthread_priority_voucher
,
2652 MACH_VOUCHER_ATTR_KEY_PTHPRIORITY
,
2655 if (kr
!= KERN_SUCCESS
) {
2659 /* return KERN_INVALID_VALUE for default value */
2660 if (content_size
< sizeof(mach_voucher_attr_recipe_t
)) {
2661 return KERN_INVALID_VALUE
;
2664 cur_content
= (mach_voucher_attr_recipe_t
) (void *) &content_data
[0];
2665 assert(cur_content
->content_size
== sizeof(ipc_pthread_priority_value_t
));
2666 memcpy(canonicalize_priority_value
, cur_content
->content
, sizeof(ipc_pthread_priority_value_t
));
2668 return KERN_SUCCESS
;
2673 * Routine: ipc_voucher_send_preprocessing
2675 * Processing of the voucher in the kmsg before sending it.
2676 * Currently use to switch PERSONA_TOKEN in case of process with
2677 * no com.apple.private.personas.propagate entitlement.
2680 ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg
)
2682 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2683 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2684 sizeof(ipc_voucher_attr_recipe_data_t
);
2685 ipc_voucher_t pre_processed_voucher
;
2686 ipc_voucher_t voucher_to_send
;
2688 int need_preprocessing
= FALSE
;
2690 if (!IP_VALID(kmsg
->ikm_voucher
) || current_task() == kernel_task
) {
2694 /* setup recipe for preprocessing of all the attributes. */
2695 pre_processed_voucher
= (ipc_voucher_t
)ip_get_kobject(kmsg
->ikm_voucher
);
2697 kr
= ipc_voucher_prepare_processing_recipe(pre_processed_voucher
,
2698 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2699 &recipe_size
, MACH_VOUCHER_ATTR_SEND_PREPROCESS
,
2700 IVAM_FLAGS_SUPPORT_SEND_PREPROCESS
, &need_preprocessing
);
2702 assert(KERN_SUCCESS
== kr
);
2704 * Only do send preprocessing if the voucher needs any pre processing.
2706 if (need_preprocessing
) {
2707 kr
= ipc_create_mach_voucher(recipes
,
2710 assert(KERN_SUCCESS
== kr
);
2711 ipc_port_release_send(kmsg
->ikm_voucher
);
2712 kmsg
->ikm_voucher
= convert_voucher_to_port(voucher_to_send
);
2717 * Routine: ipc_voucher_receive_postprocessing
2719 * Redeems the voucher attached to the kmsg.
2721 * Although it is possible to call ipc_importance_receive
2722 * here, it is called in mach_msg_receive_results and not here
2723 * in order to maintain symmetry with ipc_voucher_send_preprocessing.
2726 ipc_voucher_receive_postprocessing(
2728 mach_msg_option_t option
)
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 recv_voucher
;
2734 ipc_voucher_t sent_voucher
;
2736 int need_postprocessing
= FALSE
;
2738 if ((option
& MACH_RCV_VOUCHER
) == 0 || (!IP_VALID(kmsg
->ikm_voucher
)) ||
2739 current_task() == kernel_task
) {
2743 /* setup recipe for auto redeem of all the attributes. */
2744 sent_voucher
= (ipc_voucher_t
)ip_get_kobject(kmsg
->ikm_voucher
);
2746 kr
= ipc_voucher_prepare_processing_recipe(sent_voucher
,
2747 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2748 &recipe_size
, MACH_VOUCHER_ATTR_AUTO_REDEEM
,
2749 IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS
, &need_postprocessing
);
2751 assert(KERN_SUCCESS
== kr
);
2754 * Only do receive postprocessing if the voucher needs any post processing.
2756 if (need_postprocessing
) {
2757 kr
= ipc_create_mach_voucher(recipes
,
2760 assert(KERN_SUCCESS
== kr
);
2761 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
2762 kmsg
->ikm_header
->msgh_bits
|= (MACH_MSG_TYPE_MOVE_SEND
<< 16);
2763 ipc_port_release_send(kmsg
->ikm_voucher
);
2764 kmsg
->ikm_voucher
= convert_voucher_to_port(recv_voucher
);
2769 * Routine: ipc_voucher_prepare_processing_recipe
2771 * Check if the given voucher has an attribute which supports
2772 * the given flag and prepare a recipe to apply that supported
2775 static kern_return_t
2776 ipc_voucher_prepare_processing_recipe(
2777 ipc_voucher_t voucher
,
2778 ipc_voucher_attr_raw_recipe_array_t recipes
,
2779 ipc_voucher_attr_raw_recipe_array_size_t
*in_out_size
,
2780 mach_voucher_attr_recipe_command_t command
,
2781 ipc_voucher_attr_manager_flags flags
,
2782 int *need_processing
)
2784 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= *in_out_size
;
2785 ipc_voucher_attr_raw_recipe_array_size_t recipe_used
= 0;
2786 iv_index_t key_index
;
2787 ipc_voucher_attr_recipe_t recipe
;
2789 if (IV_NULL
== voucher
) {
2790 return KERN_INVALID_ARGUMENT
;
2793 /* Setup a recipe to copy all attributes. */
2794 if (recipe_size
< sizeof(*recipe
)) {
2795 return KERN_NO_SPACE
;
2798 *need_processing
= FALSE
;
2799 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2800 recipe
->key
= MACH_VOUCHER_ATTR_KEY_ALL
;
2801 recipe
->command
= MACH_VOUCHER_ATTR_COPY
;
2802 recipe
->previous_voucher
= voucher
;
2803 recipe
->content_size
= 0;
2804 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2806 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2807 ipc_voucher_attr_manager_t manager
;
2808 mach_voucher_attr_key_t key
;
2809 iv_index_t value_index
;
2811 /* don't output anything for a default value */
2812 value_index
= iv_lookup(voucher
, key_index
);
2813 if (IV_UNUSED_VALINDEX
== value_index
) {
2817 if (recipe_size
- recipe_used
< sizeof(*recipe
)) {
2818 return KERN_NO_SPACE
;
2821 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2824 * Get the manager for this key_index. The
2825 * existence of a non-default value for this
2826 * slot within our voucher will keep the
2827 * manager referenced during the callout.
2829 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2830 assert(IVAM_NULL
!= manager
);
2831 if (IVAM_NULL
== manager
) {
2835 /* Check if the supported flag is set in the manager */
2836 if ((manager
->ivam_flags
& flags
) == 0) {
2840 key
= iv_index_to_key(key_index
);
2843 recipe
->command
= command
;
2844 recipe
->content_size
= 0;
2845 recipe
->previous_voucher
= voucher
;
2847 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2848 *need_processing
= TRUE
;
2851 *in_out_size
= recipe_used
;
2852 return KERN_SUCCESS
;
2856 * Activity id Generation
2858 uint64_t voucher_activity_id
;
2860 #define generate_activity_id(x) \
2861 ((uint64_t)OSAddAtomic64((x), (int64_t *)&voucher_activity_id))
2864 * Routine: mach_init_activity_id
2866 * Initialize voucher activity id.
2869 mach_init_activity_id(void)
2871 voucher_activity_id
= 1;
2875 * Routine: mach_generate_activity_id
2877 * Generate a system wide voucher activity id.
2880 mach_generate_activity_id(
2881 struct mach_generate_activity_id_args
*args
)
2883 uint64_t activity_id
;
2884 kern_return_t kr
= KERN_SUCCESS
;
2886 if (args
->count
<= 0 || args
->count
> MACH_ACTIVITY_ID_COUNT_MAX
) {
2887 return KERN_INVALID_ARGUMENT
;
2890 activity_id
= generate_activity_id(args
->count
);
2891 kr
= copyout(&activity_id
, args
->activity_id
, sizeof(activity_id
));
2896 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2899 * Build-in a simple User Data Resource Manager
2901 #define USER_DATA_MAX_DATA (16*1024)
2903 struct user_data_value_element
{
2904 mach_voucher_attr_value_reference_t e_made
;
2905 mach_voucher_attr_content_size_t e_size
;
2908 queue_chain_t e_hash_link
;
2912 typedef struct user_data_value_element
*user_data_element_t
;
2915 * User Data Voucher Hash Table
2917 #define USER_DATA_HASH_BUCKETS 127
2918 #define USER_DATA_HASH_BUCKET(x) ((x) % USER_DATA_HASH_BUCKETS)
2920 static queue_head_t user_data_bucket
[USER_DATA_HASH_BUCKETS
];
2921 static lck_spin_t user_data_lock_data
;
2923 #define user_data_lock_init() \
2924 lck_spin_init(&user_data_lock_data, &ipc_lck_grp, &ipc_lck_attr)
2925 #define user_data_lock_destroy() \
2926 lck_spin_destroy(&user_data_lock_data, &ipc_lck_grp)
2927 #define user_data_lock() \
2928 lck_spin_lock_grp(&user_data_lock_data, &ipc_lck_grp)
2929 #define user_data_lock_try() \
2930 lck_spin_try_lock_grp(&user_data_lock_data, &ipc_lck_grp)
2931 #define user_data_unlock() \
2932 lck_spin_unlock(&user_data_lock_data)
2934 static kern_return_t
2935 user_data_release_value(
2936 ipc_voucher_attr_manager_t manager
,
2937 mach_voucher_attr_key_t key
,
2938 mach_voucher_attr_value_handle_t value
,
2939 mach_voucher_attr_value_reference_t sync
);
2941 static kern_return_t
2942 user_data_get_value(
2943 ipc_voucher_attr_manager_t manager
,
2944 mach_voucher_attr_key_t key
,
2945 mach_voucher_attr_recipe_command_t command
,
2946 mach_voucher_attr_value_handle_array_t prev_values
,
2947 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
2948 mach_voucher_attr_content_t content
,
2949 mach_voucher_attr_content_size_t content_size
,
2950 mach_voucher_attr_value_handle_t
*out_value
,
2951 mach_voucher_attr_value_flags_t
*out_flags
,
2952 ipc_voucher_t
*out_value_voucher
);
2954 static kern_return_t
2955 user_data_extract_content(
2956 ipc_voucher_attr_manager_t manager
,
2957 mach_voucher_attr_key_t key
,
2958 mach_voucher_attr_value_handle_array_t values
,
2959 mach_voucher_attr_value_handle_array_size_t value_count
,
2960 mach_voucher_attr_recipe_command_t
*out_command
,
2961 mach_voucher_attr_content_t out_content
,
2962 mach_voucher_attr_content_size_t
*in_out_content_size
);
2964 static kern_return_t
2966 ipc_voucher_attr_manager_t manager
,
2967 mach_voucher_attr_key_t key
,
2968 mach_voucher_attr_value_handle_array_t values
,
2969 mach_msg_type_number_t value_count
,
2970 mach_voucher_attr_command_t command
,
2971 mach_voucher_attr_content_t in_content
,
2972 mach_voucher_attr_content_size_t in_content_size
,
2973 mach_voucher_attr_content_t out_content
,
2974 mach_voucher_attr_content_size_t
*out_content_size
);
2978 ipc_voucher_attr_manager_t manager
);
2980 const struct ipc_voucher_attr_manager user_data_manager
= {
2981 .ivam_release_value
= user_data_release_value
,
2982 .ivam_get_value
= user_data_get_value
,
2983 .ivam_extract_content
= user_data_extract_content
,
2984 .ivam_command
= user_data_command
,
2985 .ivam_release
= user_data_release
,
2986 .ivam_flags
= IVAM_FLAGS_NONE
,
2989 ipc_voucher_attr_control_t user_data_control
;
2990 ipc_voucher_attr_control_t test_control
;
2992 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) && defined(MACH_VOUCHER_ATTR_KEY_TEST)
2993 #define USER_DATA_ASSERT_KEY(key) \
2994 assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key) || \
2995 MACH_VOUCHER_ATTR_KEY_TEST == (key));
2996 #elif defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
2997 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key))
2999 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_TEST == (key))
3003 * Routine: user_data_release_value
3005 * Release a made reference on a specific value managed by
3006 * this voucher attribute manager.
3008 * Must remove the element associated with this value from
3009 * the hash if this is the last know made reference.
3011 static kern_return_t
3012 user_data_release_value(
3013 ipc_voucher_attr_manager_t __assert_only manager
,
3014 mach_voucher_attr_key_t __assert_only key
,
3015 mach_voucher_attr_value_handle_t value
,
3016 mach_voucher_attr_value_reference_t sync
)
3018 user_data_element_t elem
;
3021 assert(&user_data_manager
== manager
);
3022 USER_DATA_ASSERT_KEY(key
);
3024 elem
= (user_data_element_t
)value
;
3025 hash
= elem
->e_hash
;
3028 if (sync
== elem
->e_made
) {
3029 queue_remove(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
);
3031 kfree(elem
, sizeof(*elem
) + elem
->e_size
);
3032 return KERN_SUCCESS
;
3034 assert(sync
< elem
->e_made
);
3037 return KERN_FAILURE
;
3041 * Routine: user_data_checksum
3043 * Provide a rudimentary checksum for the data presented
3044 * to these voucher attribute managers.
3048 mach_voucher_attr_content_t content
,
3049 mach_voucher_attr_content_size_t content_size
)
3051 mach_voucher_attr_content_size_t i
;
3052 iv_index_t cksum
= 0;
3054 for (i
= 0; i
< content_size
; i
++, content
++) {
3055 cksum
= (cksum
<< 8) ^ (cksum
+ *(unsigned char *)content
);
3062 * Routine: user_data_dedup
3064 * See if the content represented by this request already exists
3065 * in another user data element. If so return a made reference
3066 * to the existing element. Otherwise, create a new element and
3067 * return that (after inserting it in the hash).
3071 * A made reference on the user_data_element_t
3073 static user_data_element_t
3075 mach_voucher_attr_content_t content
,
3076 mach_voucher_attr_content_size_t content_size
)
3080 user_data_element_t elem
;
3081 user_data_element_t alloc
= NULL
;
3083 sum
= user_data_checksum(content
, content_size
);
3084 hash
= USER_DATA_HASH_BUCKET(sum
);
3088 queue_iterate(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
) {
3089 assert(elem
->e_hash
== hash
);
3091 /* if sums match... */
3092 if (elem
->e_sum
== sum
&& elem
->e_size
== content_size
) {
3095 /* and all data matches */
3096 for (i
= 0; i
< content_size
; i
++) {
3097 if (elem
->e_data
[i
] != content
[i
]) {
3101 if (i
< content_size
) {
3105 /* ... we found a match... */
3110 if (NULL
!= alloc
) {
3111 kfree(alloc
, sizeof(*alloc
) + content_size
);
3118 if (NULL
== alloc
) {
3121 alloc
= (user_data_element_t
)kalloc(sizeof(*alloc
) + content_size
);
3123 alloc
->e_size
= content_size
;
3125 alloc
->e_hash
= hash
;
3126 memcpy(alloc
->e_data
, content
, content_size
);
3130 queue_enter(&user_data_bucket
[hash
], alloc
, user_data_element_t
, e_hash_link
);
3136 static kern_return_t
3137 user_data_get_value(
3138 ipc_voucher_attr_manager_t __assert_only manager
,
3139 mach_voucher_attr_key_t __assert_only key
,
3140 mach_voucher_attr_recipe_command_t command
,
3141 mach_voucher_attr_value_handle_array_t prev_values
,
3142 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3143 mach_voucher_attr_content_t content
,
3144 mach_voucher_attr_content_size_t content_size
,
3145 mach_voucher_attr_value_handle_t
*out_value
,
3146 mach_voucher_attr_value_flags_t
*out_flags
,
3147 ipc_voucher_t
*out_value_voucher
)
3149 user_data_element_t elem
;
3151 assert(&user_data_manager
== manager
);
3152 USER_DATA_ASSERT_KEY(key
);
3154 /* never an out voucher */
3155 *out_value_voucher
= IPC_VOUCHER_NULL
;
3156 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
3159 case MACH_VOUCHER_ATTR_REDEEM
:
3161 /* redeem of previous values is the value */
3162 if (0 < prev_value_count
) {
3163 elem
= (user_data_element_t
)prev_values
[0];
3164 assert(0 < elem
->e_made
);
3166 *out_value
= prev_values
[0];
3167 return KERN_SUCCESS
;
3170 /* redeem of default is default */
3172 return KERN_SUCCESS
;
3174 case MACH_VOUCHER_ATTR_USER_DATA_STORE
:
3175 if (USER_DATA_MAX_DATA
< content_size
) {
3176 return KERN_RESOURCE_SHORTAGE
;
3179 /* empty is the default */
3180 if (0 == content_size
) {
3182 return KERN_SUCCESS
;
3185 elem
= user_data_dedup(content
, content_size
);
3186 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
3187 return KERN_SUCCESS
;
3190 /* every other command is unknown */
3191 return KERN_INVALID_ARGUMENT
;
3195 static kern_return_t
3196 user_data_extract_content(
3197 ipc_voucher_attr_manager_t __assert_only manager
,
3198 mach_voucher_attr_key_t __assert_only key
,
3199 mach_voucher_attr_value_handle_array_t values
,
3200 mach_voucher_attr_value_handle_array_size_t value_count
,
3201 mach_voucher_attr_recipe_command_t
*out_command
,
3202 mach_voucher_attr_content_t out_content
,
3203 mach_voucher_attr_content_size_t
*in_out_content_size
)
3205 mach_voucher_attr_content_size_t size
= 0;
3206 user_data_element_t elem
;
3209 assert(&user_data_manager
== manager
);
3210 USER_DATA_ASSERT_KEY(key
);
3212 /* concatenate the stored data items */
3213 for (i
= 0; i
< value_count
&& *in_out_content_size
> 0; i
++) {
3214 elem
= (user_data_element_t
)values
[i
];
3215 assert(USER_DATA_MAX_DATA
>= elem
->e_size
);
3217 if (size
+ elem
->e_size
> *in_out_content_size
) {
3218 return KERN_NO_SPACE
;
3221 memcpy(&out_content
[size
], elem
->e_data
, elem
->e_size
);
3222 size
+= elem
->e_size
;
3224 *out_command
= MACH_VOUCHER_ATTR_BITS_STORE
;
3225 *in_out_content_size
= size
;
3226 return KERN_SUCCESS
;
3229 static kern_return_t
3231 ipc_voucher_attr_manager_t __assert_only manager
,
3232 mach_voucher_attr_key_t __assert_only key
,
3233 mach_voucher_attr_value_handle_array_t __unused values
,
3234 mach_msg_type_number_t __unused value_count
,
3235 mach_voucher_attr_command_t __unused command
,
3236 mach_voucher_attr_content_t __unused in_content
,
3237 mach_voucher_attr_content_size_t __unused in_content_size
,
3238 mach_voucher_attr_content_t __unused out_content
,
3239 mach_voucher_attr_content_size_t __unused
*out_content_size
)
3241 assert(&user_data_manager
== manager
);
3242 USER_DATA_ASSERT_KEY(key
);
3243 return KERN_FAILURE
;
3248 ipc_voucher_attr_manager_t manager
)
3250 if (manager
!= &user_data_manager
) {
3254 panic("Voucher user-data manager released");
3257 static int user_data_manager_inited
= 0;
3260 user_data_attr_manager_init()
3264 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
3265 if ((user_data_manager_inited
& 0x1) != 0x1) {
3266 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3267 (mach_voucher_attr_value_handle_t
)0,
3268 MACH_VOUCHER_ATTR_KEY_USER_DATA
,
3269 &user_data_control
);
3270 if (KERN_SUCCESS
!= kr
) {
3271 printf("Voucher user-data manager register(USER-DATA) returned %d", kr
);
3273 user_data_manager_inited
|= 0x1;
3277 #if defined(MACH_VOUCHER_ATTR_KEY_TEST)
3278 if ((user_data_manager_inited
& 0x2) != 0x2) {
3279 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3280 (mach_voucher_attr_value_handle_t
)0,
3281 MACH_VOUCHER_ATTR_KEY_TEST
,
3283 if (KERN_SUCCESS
!= kr
) {
3284 printf("Voucher user-data manager register(TEST) returned %d", kr
);
3286 user_data_manager_inited
|= 0x2;
3290 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
3293 for (i
= 0; i
< USER_DATA_HASH_BUCKETS
; i
++) {
3294 queue_init(&user_data_bucket
[i
]);
3297 user_data_lock_init();
3301 #endif /* MACH_DEBUG */