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 uintptr_t voucher
= (uintptr_t) port
->ip_kobject
;
383 * No need to lock because we have a reference on the
384 * port, and if it is a true voucher port, that reference
385 * keeps the voucher bound to the port (and active).
387 if (ip_kotype(port
) == IKOT_VOUCHER
) {
391 return (uintptr_t)IV_NULL
;
395 * Routine: convert_port_to_voucher
397 * Convert from a port to a voucher.
398 * Doesn't consume the port [send-right] ref;
399 * produces a voucher ref, which may be null.
401 * Caller has a send-right reference to port.
402 * Port may or may not be locked.
405 convert_port_to_voucher(
408 if (IP_VALID(port
)) {
409 zone_require(port
, ipc_object_zones
[IOT_PORT
]);
410 ipc_voucher_t voucher
= (ipc_voucher_t
) port
->ip_kobject
;
413 * No need to lock because we have a reference on the
414 * port, and if it is a true voucher port, that reference
415 * keeps the voucher bound to the port (and active).
417 if (ip_kotype(port
) != IKOT_VOUCHER
) {
421 require_ip_active(port
);
423 zone_require(voucher
, ipc_voucher_zone
);
424 ipc_voucher_reference(voucher
);
431 * Routine: convert_port_name_to_voucher
433 * Convert from a port name in the current space to a voucher.
434 * Produces a voucher ref, which may be null.
440 convert_port_name_to_voucher(
441 mach_port_name_t voucher_name
)
447 if (MACH_PORT_VALID(voucher_name
)) {
448 kr
= ipc_port_translate_send(current_space(), voucher_name
, &port
);
449 if (KERN_SUCCESS
!= kr
) {
453 iv
= convert_port_to_voucher(port
);
462 ipc_voucher_reference(ipc_voucher_t voucher
)
464 if (IPC_VOUCHER_NULL
== voucher
) {
468 iv_reference(voucher
);
472 ipc_voucher_release(ipc_voucher_t voucher
)
474 if (IPC_VOUCHER_NULL
!= voucher
) {
480 * Routine: ipc_voucher_notify
482 * Called whenever the Mach port system detects no-senders
483 * on the voucher port.
486 ipc_voucher_notify(mach_msg_header_t
*msg
)
488 mach_no_senders_notification_t
*notification
= (void *)msg
;
489 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
491 require_ip_active(port
);
492 assert(IKOT_VOUCHER
== ip_kotype(port
));
494 /* consume the reference donated by convert_voucher_to_port */
495 zone_require((ipc_voucher_t
)port
->ip_kobject
, ipc_voucher_zone
);
496 ipc_voucher_release((ipc_voucher_t
)port
->ip_kobject
);
500 * Convert a voucher to a port.
503 convert_voucher_to_port(ipc_voucher_t voucher
)
505 if (IV_NULL
== voucher
) {
509 zone_require(voucher
, ipc_voucher_zone
);
510 assert(os_ref_get_count(&voucher
->iv_refs
) > 0);
513 * make a send right and donate our reference for ipc_voucher_notify
514 * if this is the first send right
516 if (!ipc_kobject_make_send_lazy_alloc_port(&voucher
->iv_port
,
517 (ipc_kobject_t
)voucher
, IKOT_VOUCHER
)) {
518 ipc_voucher_release(voucher
);
520 return voucher
->iv_port
;
523 #define ivace_reset_data(ivace_elem, next_index) { \
524 (ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE; \
525 (ivace_elem)->ivace_refs = 0; \
526 (ivace_elem)->ivace_persist = 0; \
527 (ivace_elem)->ivace_made = 0; \
528 (ivace_elem)->ivace_free = TRUE; \
529 (ivace_elem)->ivace_releasing = FALSE; \
530 (ivace_elem)->ivace_layered = 0; \
531 (ivace_elem)->ivace_index = IV_HASH_END; \
532 (ivace_elem)->ivace_next = (next_index); \
535 #define ivace_copy_data(ivace_src_elem, ivace_dst_elem) { \
536 (ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
537 (ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs; \
538 (ivace_dst_elem)->ivace_persist = (ivace_src_elem)->ivace_persist; \
539 (ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made; \
540 (ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free; \
541 (ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered; \
542 (ivace_dst_elem)->ivace_releasing = (ivace_src_elem)->ivace_releasing; \
543 (ivace_dst_elem)->ivace_index = (ivace_src_elem)->ivace_index; \
544 (ivace_dst_elem)->ivace_next = (ivace_src_elem)->ivace_next; \
547 ipc_voucher_attr_control_t
548 ivac_alloc(iv_index_t key_index
)
550 ipc_voucher_attr_control_t ivac
;
555 ivac
= (ipc_voucher_attr_control_t
)zalloc(ipc_voucher_attr_control_zone
);
556 if (IVAC_NULL
== ivac
) {
560 os_ref_init(&ivac
->ivac_refs
, &ivac_refgrp
);
561 ivac
->ivac_is_growing
= FALSE
;
562 ivac
->ivac_port
= IP_NULL
;
564 /* start with just the inline table */
565 table
= (ivac_entry_t
) kalloc(IVAC_ENTRIES_MIN
* sizeof(ivac_entry
));
566 ivac
->ivac_table
= table
;
567 ivac
->ivac_table_size
= IVAC_ENTRIES_MIN
;
568 ivac
->ivac_init_table_size
= IVAC_ENTRIES_MIN
;
569 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
570 ivace_reset_data(&table
[i
], i
+ 1);
573 /* the default table entry is never on freelist */
574 table
[0].ivace_next
= IV_HASH_END
;
575 table
[0].ivace_free
= FALSE
;
576 table
[i
- 1].ivace_next
= IV_FREELIST_END
;
577 ivac
->ivac_freelist
= 1;
578 ivac_lock_init(ivac
);
579 ivac
->ivac_key_index
= key_index
;
585 ivac_dealloc(ipc_voucher_attr_control_t ivac
)
587 ipc_voucher_attr_manager_t ivam
= IVAM_NULL
;
588 iv_index_t key_index
= ivac
->ivac_key_index
;
589 ipc_port_t port
= ivac
->ivac_port
;
593 * If the control is in the global table, we
594 * have to remove it from there before we (re)confirm
595 * that the reference count is still zero.
598 if (os_ref_get_count(&ivac
->ivac_refs
) > 0) {
603 /* take it out of the global table */
604 if (iv_global_table
[key_index
].ivgte_control
== ivac
) {
605 ivam
= iv_global_table
[key_index
].ivgte_manager
;
606 iv_global_table
[key_index
].ivgte_manager
= IVAM_NULL
;
607 iv_global_table
[key_index
].ivgte_control
= IVAC_NULL
;
608 iv_global_table
[key_index
].ivgte_key
= MACH_VOUCHER_ATTR_KEY_NONE
;
612 /* release the reference held on the resource manager */
613 if (IVAM_NULL
!= ivam
) {
614 (ivam
->ivam_release
)(ivam
);
618 * if a port was allocated for this voucher,
619 * it must not have any remaining send rights,
620 * because the port's reference on the voucher
621 * is gone. We can just discard it now.
623 if (IP_VALID(port
)) {
624 require_ip_active(port
);
625 assert(port
->ip_srights
== 0);
627 ipc_port_dealloc_kernel(port
);
631 * the resource manager's control reference and all references
632 * held by the specific value caches are gone, so free the
636 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
637 if (ivac
->ivac_table
[i
].ivace_refs
!= 0) {
638 panic("deallocing a resource manager with live refs to its attr values\n");
642 kfree(ivac
->ivac_table
, ivac
->ivac_table_size
* sizeof(*ivac
->ivac_table
));
643 ivac_lock_destroy(ivac
);
644 zfree(ipc_voucher_attr_control_zone
, ivac
);
648 ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control
)
650 ivac_reference(control
);
654 ipc_voucher_attr_control_release(ipc_voucher_attr_control_t control
)
656 ivac_release(control
);
660 * Routine: convert_port_to_voucher_attr_control reference
662 * Convert from a port to a voucher attribute control.
663 * Doesn't consume the port ref; produces a voucher ref,
668 ipc_voucher_attr_control_t
669 convert_port_to_voucher_attr_control(
672 if (IP_VALID(port
)) {
673 zone_require(port
, ipc_object_zones
[IOT_PORT
]);
674 ipc_voucher_attr_control_t ivac
= (ipc_voucher_attr_control_t
) port
->ip_kobject
;
677 * No need to lock because we have a reference on the
678 * port, and if it is a true voucher control port,
679 * that reference keeps the voucher bound to the port
682 if (ip_kotype(port
) != IKOT_VOUCHER_ATTR_CONTROL
) {
685 require_ip_active(port
);
687 zone_require(ivac
, ipc_voucher_attr_control_zone
);
688 ivac_reference(ivac
);
695 * Routine: ipc_voucher_notify
697 * Called whenever the Mach port system detects no-senders
698 * on the voucher attr control port.
701 ipc_voucher_attr_control_notify(mach_msg_header_t
*msg
)
703 mach_no_senders_notification_t
*notification
= (void *)msg
;
704 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
706 require_ip_active(port
);
707 assert(IKOT_VOUCHER_ATTR_CONTROL
== ip_kotype(port
));
709 /* release the reference donated by convert_voucher_attr_control_to_port */
710 ivac_release((ipc_voucher_attr_control_t
)port
->ip_kobject
);
714 * Convert a voucher attr control to a port.
717 convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control
)
719 if (IVAC_NULL
== control
) {
723 zone_require(control
, ipc_voucher_attr_control_zone
);
726 * make a send right and donate our reference for
727 * ipc_voucher_attr_control_notify if this is the first send right
729 if (!ipc_kobject_make_send_lazy_alloc_port(&control
->ivac_port
,
730 (ipc_kobject_t
)control
, IKOT_VOUCHER_ATTR_CONTROL
)) {
731 ivac_release(control
);
733 return control
->ivac_port
;
737 * Look up the values for a given <key, index> pair.
741 iv_index_t key_index
,
742 iv_index_t value_index
,
743 mach_voucher_attr_value_handle_array_t values
,
744 mach_voucher_attr_value_handle_array_size_t
*count
)
746 ipc_voucher_attr_control_t ivac
;
749 if (IV_UNUSED_VALINDEX
== value_index
||
750 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
<= key_index
) {
755 ivac
= iv_global_table
[key_index
].ivgte_control
;
756 assert(IVAC_NULL
!= ivac
);
759 * Get the entry and then the linked values.
762 assert(value_index
< ivac
->ivac_table_size
);
763 ivace
= &ivac
->ivac_table
[value_index
];
766 * TODO: support chained values (for effective vouchers).
768 assert(ivace
->ivace_refs
> 0);
769 values
[0] = ivace
->ivace_value
;
775 * ivac_grow_table - Allocate a bigger table of attribute values
777 * Conditions: ivac is locked on entry and again on return
780 ivac_grow_table(ipc_voucher_attr_control_t ivac
)
784 /* NOTE: do not modify *_table and *_size values once set */
785 ivac_entry_t new_table
= NULL
, old_table
= NULL
;
786 iv_index_t new_size
, old_size
;
788 if (ivac
->ivac_is_growing
) {
793 ivac
->ivac_is_growing
= 1;
794 if (ivac
->ivac_table_size
>= IVAC_ENTRIES_MAX
) {
795 panic("Cannot grow ipc space beyond IVAC_ENTRIES_MAX. Some process is leaking vouchers");
799 old_size
= ivac
->ivac_table_size
;
802 new_size
= old_size
* 2;
804 assert(new_size
> old_size
);
805 assert(new_size
< IVAC_ENTRIES_MAX
);
807 new_table
= kalloc(sizeof(ivac_entry
) * new_size
);
809 panic("Failed to grow ivac table to size %d\n", new_size
);
813 /* setup the free list for new entries */
814 for (i
= old_size
; i
< new_size
; i
++) {
815 ivace_reset_data(&new_table
[i
], i
+ 1);
820 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
821 ivace_copy_data(&ivac
->ivac_table
[i
], &new_table
[i
]);
824 old_table
= ivac
->ivac_table
;
826 ivac
->ivac_table
= new_table
;
827 ivac
->ivac_table_size
= new_size
;
829 /* adding new free entries at head of freelist */
830 ivac
->ivac_table
[new_size
- 1].ivace_next
= ivac
->ivac_freelist
;
831 ivac
->ivac_freelist
= old_size
;
832 ivac
->ivac_is_growing
= 0;
837 kfree(old_table
, old_size
* sizeof(ivac_entry
));
843 * ivace_reference_by_index
845 * Take an additional reference on the <key_index, val_index>
846 * cached value. It is assumed the caller already holds a
847 * reference to the same cached key-value pair.
850 ivace_reference_by_index(
851 iv_index_t key_index
,
852 iv_index_t val_index
)
854 ipc_voucher_attr_control_t ivac
;
857 if (IV_UNUSED_VALINDEX
== val_index
) {
861 ivgt_lookup(key_index
, FALSE
, NULL
, &ivac
);
862 assert(IVAC_NULL
!= ivac
);
865 assert(val_index
< ivac
->ivac_table_size
);
866 ivace
= &ivac
->ivac_table
[val_index
];
868 assert(0xdeadc0dedeadc0de != ivace
->ivace_value
);
869 assert(0 < ivace
->ivace_refs
);
870 assert(!ivace
->ivace_free
);
872 /* Take ref only on non-persistent values */
873 if (!ivace
->ivace_persist
) {
881 * Look up the values for a given <key, index> pair.
883 * Consumes a reference on the passed voucher control.
884 * Either it is donated to a newly-created value cache
885 * or it is released (if we piggy back on an existing
886 * value cache entry).
889 ivace_reference_by_value(
890 ipc_voucher_attr_control_t ivac
,
891 mach_voucher_attr_value_handle_t value
,
892 mach_voucher_attr_value_flags_t flag
)
894 ivac_entry_t ivace
= IVACE_NULL
;
895 iv_index_t hash_index
;
898 if (IVAC_NULL
== ivac
) {
899 return IV_UNUSED_VALINDEX
;
904 hash_index
= IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
905 index
= ivac
->ivac_table
[hash_index
].ivace_index
;
906 while (index
!= IV_HASH_END
) {
907 assert(index
< ivac
->ivac_table_size
);
908 ivace
= &ivac
->ivac_table
[index
];
909 assert(!ivace
->ivace_free
);
911 if (ivace
->ivace_value
== value
) {
915 assert(ivace
->ivace_next
!= index
);
916 index
= ivace
->ivace_next
;
920 if (index
!= IV_HASH_END
) {
921 /* only add reference on non-persistent value */
922 if (!ivace
->ivace_persist
) {
932 /* insert new entry in the table */
933 index
= ivac
->ivac_freelist
;
934 if (IV_FREELIST_END
== index
) {
936 ivac_grow_table(ivac
);
940 /* take the entry off the freelist */
941 ivace
= &ivac
->ivac_table
[index
];
942 ivac
->ivac_freelist
= ivace
->ivace_next
;
944 /* initialize the new entry */
945 ivace
->ivace_value
= value
;
946 ivace
->ivace_refs
= 1;
947 ivace
->ivace_made
= 1;
948 ivace
->ivace_free
= FALSE
;
949 ivace
->ivace_persist
= (flag
& MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
) ? TRUE
: FALSE
;
951 /* insert the new entry in the proper hash chain */
952 ivace
->ivace_next
= ivac
->ivac_table
[hash_index
].ivace_index
;
953 ivac
->ivac_table
[hash_index
].ivace_index
= index
;
956 /* donated passed in ivac reference to new entry */
962 * Release a reference on the given <key_index, value_index> pair.
964 * Conditions: called with nothing locked, as it may cause
965 * callouts and/or messaging to the resource
970 iv_index_t key_index
,
971 iv_index_t value_index
)
973 ipc_voucher_attr_control_t ivac
;
974 ipc_voucher_attr_manager_t ivam
;
975 mach_voucher_attr_value_handle_t value
;
976 mach_voucher_attr_value_reference_t made
;
977 mach_voucher_attr_key_t key
;
978 iv_index_t hash_index
;
982 /* cant release the default value */
983 if (IV_UNUSED_VALINDEX
== value_index
) {
987 ivgt_lookup(key_index
, FALSE
, &ivam
, &ivac
);
988 assert(IVAC_NULL
!= ivac
);
989 assert(IVAM_NULL
!= ivam
);
992 assert(value_index
< ivac
->ivac_table_size
);
993 ivace
= &ivac
->ivac_table
[value_index
];
995 assert(0 < ivace
->ivace_refs
);
997 /* cant release persistent values */
998 if (ivace
->ivace_persist
) {
1003 if (0 < --ivace
->ivace_refs
) {
1008 key
= iv_index_to_key(key_index
);
1009 assert(MACH_VOUCHER_ATTR_KEY_NONE
!= key
);
1012 * if last return reply is still pending,
1013 * let it handle this later return when
1014 * the previous reply comes in.
1016 if (ivace
->ivace_releasing
) {
1021 /* claim releasing */
1022 ivace
->ivace_releasing
= TRUE
;
1023 value
= ivace
->ivace_value
;
1026 assert(value
== ivace
->ivace_value
);
1027 assert(!ivace
->ivace_free
);
1028 made
= ivace
->ivace_made
;
1031 /* callout to manager's release_value */
1032 kr
= (ivam
->ivam_release_value
)(ivam
, key
, value
, made
);
1034 /* recalculate entry address as table may have changed */
1036 ivace
= &ivac
->ivac_table
[value_index
];
1037 assert(value
== ivace
->ivace_value
);
1040 * new made values raced with this return. If the
1041 * manager OK'ed the prior release, we have to start
1042 * the made numbering over again (pretend the race
1043 * didn't happen). If the entry has zero refs again,
1044 * re-drive the release.
1046 if (ivace
->ivace_made
!= made
) {
1047 if (KERN_SUCCESS
== kr
) {
1048 ivace
->ivace_made
-= made
;
1051 if (0 == ivace
->ivace_refs
) {
1055 ivace
->ivace_releasing
= FALSE
;
1060 * If the manager returned FAILURE, someone took a
1061 * reference on the value but have not updated the ivace,
1062 * release the lock and return since thread who got
1063 * the new reference will update the ivace and will have
1064 * non-zero reference on the value.
1066 if (KERN_SUCCESS
!= kr
) {
1067 ivace
->ivace_releasing
= FALSE
;
1073 assert(0 == ivace
->ivace_refs
);
1076 * going away - remove entry from its hash
1077 * If its at the head of the hash bucket list (common), unchain
1078 * at the head. Otherwise walk the chain until the next points
1079 * at this entry, and remove it from the the list there.
1081 hash_index
= iv_hash_value(key_index
, value
);
1082 if (ivac
->ivac_table
[hash_index
].ivace_index
== value_index
) {
1083 ivac
->ivac_table
[hash_index
].ivace_index
= ivace
->ivace_next
;
1085 hash_index
= ivac
->ivac_table
[hash_index
].ivace_index
;
1086 assert(IV_HASH_END
!= hash_index
);
1087 while (ivac
->ivac_table
[hash_index
].ivace_next
!= value_index
) {
1088 hash_index
= ivac
->ivac_table
[hash_index
].ivace_next
;
1089 assert(IV_HASH_END
!= hash_index
);
1091 ivac
->ivac_table
[hash_index
].ivace_next
= ivace
->ivace_next
;
1094 /* Put this entry on the freelist */
1095 ivace
->ivace_value
= 0xdeadc0dedeadc0de;
1096 ivace
->ivace_releasing
= FALSE
;
1097 ivace
->ivace_free
= TRUE
;
1098 ivace
->ivace_made
= 0;
1099 ivace
->ivace_next
= ivac
->ivac_freelist
;
1100 ivac
->ivac_freelist
= value_index
;
1103 /* release the reference this value held on its cache control */
1113 * Lookup an entry in the global table from the context of a manager
1114 * registration. Adds a reference to the control to keep the results
1115 * around (if needed).
1117 * Because of the calling point, we can't be sure the manager is
1118 * [fully] registered yet. So, we must hold the global table lock
1119 * during the lookup to synchronize with in-parallel registrations
1120 * (and possible table growth).
1123 ivgt_lookup(iv_index_t key_index
,
1124 boolean_t take_reference
,
1125 ipc_voucher_attr_manager_t
*manager
,
1126 ipc_voucher_attr_control_t
*control
)
1128 ipc_voucher_attr_control_t ivac
;
1130 if (key_index
< MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
) {
1132 if (NULL
!= manager
) {
1133 *manager
= iv_global_table
[key_index
].ivgte_manager
;
1135 ivac
= iv_global_table
[key_index
].ivgte_control
;
1136 if (IVAC_NULL
!= ivac
) {
1137 assert(key_index
== ivac
->ivac_key_index
);
1138 if (take_reference
) {
1139 assert(NULL
!= control
);
1140 ivac_reference(ivac
);
1144 if (NULL
!= control
) {
1148 if (NULL
!= manager
) {
1149 *manager
= IVAM_NULL
;
1151 if (NULL
!= control
) {
1152 *control
= IVAC_NULL
;
1158 * Routine: ipc_replace_voucher_value
1160 * Replace the <voucher, key> value with the results of
1161 * running the supplied command through the resource
1162 * manager's get-value callback.
1164 * Nothing locked (may invoke user-space repeatedly).
1165 * Caller holds references on voucher and previous voucher.
1167 static kern_return_t
1168 ipc_replace_voucher_value(
1169 ipc_voucher_t voucher
,
1170 mach_voucher_attr_key_t key
,
1171 mach_voucher_attr_recipe_command_t command
,
1172 ipc_voucher_t prev_voucher
,
1173 mach_voucher_attr_content_t content
,
1174 mach_voucher_attr_content_size_t content_size
)
1176 mach_voucher_attr_value_handle_t previous_vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1177 mach_voucher_attr_value_handle_array_size_t previous_vals_count
;
1178 mach_voucher_attr_value_handle_t new_value
;
1179 mach_voucher_attr_value_flags_t new_flag
;
1180 ipc_voucher_t new_value_voucher
;
1181 ipc_voucher_attr_manager_t ivam
;
1182 ipc_voucher_attr_control_t ivac
;
1183 iv_index_t prev_val_index
;
1184 iv_index_t save_val_index
;
1185 iv_index_t val_index
;
1186 iv_index_t key_index
;
1190 * Get the manager for this key_index.
1191 * Returns a reference on the control.
1193 key_index
= iv_key_to_index(key
);
1194 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1195 if (IVAM_NULL
== ivam
) {
1196 return KERN_INVALID_ARGUMENT
;
1199 /* save the current value stored in the forming voucher */
1200 save_val_index
= iv_lookup(voucher
, key_index
);
1203 * Get the previous value(s) for this key creation.
1204 * If a previous voucher is specified, they come from there.
1205 * Otherwise, they come from the intermediate values already
1206 * in the forming voucher.
1208 prev_val_index
= (IV_NULL
!= prev_voucher
) ?
1209 iv_lookup(prev_voucher
, key_index
) :
1211 ivace_lookup_values(key_index
, prev_val_index
,
1212 previous_vals
, &previous_vals_count
);
1214 /* Call out to resource manager to get new value */
1215 new_value_voucher
= IV_NULL
;
1216 kr
= (ivam
->ivam_get_value
)(
1218 previous_vals
, previous_vals_count
,
1219 content
, content_size
,
1220 &new_value
, &new_flag
, &new_value_voucher
);
1221 if (KERN_SUCCESS
!= kr
) {
1226 /* TODO: value insertion from returned voucher */
1227 if (IV_NULL
!= new_value_voucher
) {
1228 iv_release(new_value_voucher
);
1232 * Find or create a slot in the table associated
1233 * with this attribute value. The ivac reference
1234 * is transferred to a new value, or consumed if
1235 * we find a matching existing value.
1237 val_index
= ivace_reference_by_value(ivac
, new_value
, new_flag
);
1238 iv_set(voucher
, key_index
, val_index
);
1241 * release saved old value from the newly forming voucher
1242 * This is saved until the end to avoid churning the
1243 * release logic in cases where the same value is returned
1244 * as was there before.
1246 ivace_release(key_index
, save_val_index
);
1248 return KERN_SUCCESS
;
1252 * Routine: ipc_directly_replace_voucher_value
1254 * Replace the <voucher, key> value with the value-handle
1255 * supplied directly by the attribute manager.
1258 * Caller holds references on voucher.
1259 * A made reference to the value-handle is donated by the caller.
1261 static kern_return_t
1262 ipc_directly_replace_voucher_value(
1263 ipc_voucher_t voucher
,
1264 mach_voucher_attr_key_t key
,
1265 mach_voucher_attr_value_handle_t new_value
)
1267 ipc_voucher_attr_manager_t ivam
;
1268 ipc_voucher_attr_control_t ivac
;
1269 iv_index_t save_val_index
;
1270 iv_index_t val_index
;
1271 iv_index_t key_index
;
1274 * Get the manager for this key_index.
1275 * Returns a reference on the control.
1277 key_index
= iv_key_to_index(key
);
1278 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1279 if (IVAM_NULL
== ivam
) {
1280 return KERN_INVALID_ARGUMENT
;
1283 /* save the current value stored in the forming voucher */
1284 save_val_index
= iv_lookup(voucher
, key_index
);
1287 * Find or create a slot in the table associated
1288 * with this attribute value. The ivac reference
1289 * is transferred to a new value, or consumed if
1290 * we find a matching existing value.
1292 val_index
= ivace_reference_by_value(ivac
, new_value
,
1293 MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
);
1294 iv_set(voucher
, key_index
, val_index
);
1297 * release saved old value from the newly forming voucher
1298 * This is saved until the end to avoid churning the
1299 * release logic in cases where the same value is returned
1300 * as was there before.
1302 ivace_release(key_index
, save_val_index
);
1304 return KERN_SUCCESS
;
1307 static kern_return_t
1308 ipc_execute_voucher_recipe_command(
1309 ipc_voucher_t voucher
,
1310 mach_voucher_attr_key_t key
,
1311 mach_voucher_attr_recipe_command_t command
,
1312 ipc_voucher_t prev_iv
,
1313 mach_voucher_attr_content_t content
,
1314 mach_voucher_attr_content_size_t content_size
,
1317 iv_index_t prev_val_index
;
1318 iv_index_t val_index
;
1323 * MACH_VOUCHER_ATTR_COPY
1324 * Copy the attribute(s) from the previous voucher to the new
1325 * one. A wildcard key is an acceptable value - indicating a
1326 * desire to copy all the attribute values from the previous
1329 case MACH_VOUCHER_ATTR_COPY
:
1331 /* no recipe data on a copy */
1332 if (0 < content_size
) {
1333 return KERN_INVALID_ARGUMENT
;
1336 /* nothing to copy from? - done */
1337 if (IV_NULL
== prev_iv
) {
1338 return KERN_SUCCESS
;
1341 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1342 iv_index_t limit
, j
;
1344 /* reconcile possible difference in voucher sizes */
1345 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1346 prev_iv
->iv_table_size
:
1347 voucher
->iv_table_size
;
1349 /* wildcard matching */
1350 for (j
= 0; j
< limit
; j
++) {
1351 /* release old value being replaced */
1352 val_index
= iv_lookup(voucher
, j
);
1353 ivace_release(j
, val_index
);
1355 /* replace with reference to prev voucher's value */
1356 prev_val_index
= iv_lookup(prev_iv
, j
);
1357 ivace_reference_by_index(j
, prev_val_index
);
1358 iv_set(voucher
, j
, prev_val_index
);
1361 iv_index_t key_index
;
1363 /* copy just one key */
1364 key_index
= iv_key_to_index(key
);
1365 if (ivgt_keys_in_use
< key_index
) {
1366 return KERN_INVALID_ARGUMENT
;
1369 /* release old value being replaced */
1370 val_index
= iv_lookup(voucher
, key_index
);
1371 ivace_release(key_index
, val_index
);
1373 /* replace with reference to prev voucher's value */
1374 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1375 ivace_reference_by_index(key_index
, prev_val_index
);
1376 iv_set(voucher
, key_index
, prev_val_index
);
1381 * MACH_VOUCHER_ATTR_REMOVE
1382 * Remove the attribute(s) from the under construction voucher.
1383 * A wildcard key is an acceptable value - indicating a desire
1384 * to remove all the attribute values set up so far in the voucher.
1385 * If a previous voucher is specified, only remove the value it
1386 * it matches the value in the previous voucher.
1388 case MACH_VOUCHER_ATTR_REMOVE
:
1389 /* no recipe data on a remove */
1390 if (0 < content_size
) {
1391 return KERN_INVALID_ARGUMENT
;
1394 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1395 iv_index_t limit
, j
;
1397 /* reconcile possible difference in voucher sizes */
1398 limit
= (IV_NULL
== prev_iv
) ? voucher
->iv_table_size
:
1399 ((prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1400 prev_iv
->iv_table_size
: voucher
->iv_table_size
);
1402 /* wildcard matching */
1403 for (j
= 0; j
< limit
; j
++) {
1404 val_index
= iv_lookup(voucher
, j
);
1406 /* If not matched in previous, skip */
1407 if (IV_NULL
!= prev_iv
) {
1408 prev_val_index
= iv_lookup(prev_iv
, j
);
1409 if (val_index
!= prev_val_index
) {
1413 /* release and clear */
1414 ivace_release(j
, val_index
);
1415 iv_set(voucher
, j
, IV_UNUSED_VALINDEX
);
1418 iv_index_t key_index
;
1420 /* copy just one key */
1421 key_index
= iv_key_to_index(key
);
1422 if (ivgt_keys_in_use
< key_index
) {
1423 return KERN_INVALID_ARGUMENT
;
1426 val_index
= iv_lookup(voucher
, key_index
);
1428 /* If not matched in previous, skip */
1429 if (IV_NULL
!= prev_iv
) {
1430 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1431 if (val_index
!= prev_val_index
) {
1436 /* release and clear */
1437 ivace_release(key_index
, val_index
);
1438 iv_set(voucher
, key_index
, IV_UNUSED_VALINDEX
);
1443 * MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
1444 * Use key-privilege to set a value handle for the attribute directly,
1445 * rather than triggering a callback into the attribute manager to
1446 * interpret a recipe to generate the value handle.
1448 case MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
:
1450 mach_voucher_attr_value_handle_t new_value
;
1452 if (sizeof(mach_voucher_attr_value_handle_t
) != content_size
) {
1453 return KERN_INVALID_ARGUMENT
;
1456 new_value
= *(mach_voucher_attr_value_handle_t
*)(void *)content
;
1457 kr
= ipc_directly_replace_voucher_value(voucher
,
1459 if (KERN_SUCCESS
!= kr
) {
1463 return KERN_INVALID_CAPABILITY
;
1468 * MACH_VOUCHER_ATTR_REDEEM
1469 * Redeem the attribute(s) from the previous voucher for a possibly
1470 * new value in the new voucher. A wildcard key is an acceptable value,
1471 * indicating a desire to redeem all the values.
1473 case MACH_VOUCHER_ATTR_REDEEM
:
1475 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1476 iv_index_t limit
, j
;
1478 /* reconcile possible difference in voucher sizes */
1479 if (IV_NULL
!= prev_iv
) {
1480 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1481 prev_iv
->iv_table_size
:
1482 voucher
->iv_table_size
;
1484 limit
= voucher
->iv_table_size
;
1487 /* wildcard matching */
1488 for (j
= 0; j
< limit
; j
++) {
1489 mach_voucher_attr_key_t j_key
;
1491 j_key
= iv_index_to_key(j
);
1493 /* skip non-existent managers */
1494 if (MACH_VOUCHER_ATTR_KEY_NONE
== j_key
) {
1498 /* get the new value from redeem (skip empty previous) */
1499 kr
= ipc_replace_voucher_value(voucher
,
1505 if (KERN_SUCCESS
!= kr
) {
1511 /* fall thru for single key redemption */
1515 * Replace the current value for the <voucher, key> pair with whatever
1516 * value the resource manager returns for the command and recipe
1517 * combination provided.
1520 kr
= ipc_replace_voucher_value(voucher
,
1526 if (KERN_SUCCESS
!= kr
) {
1532 return KERN_SUCCESS
;
1536 * Routine: iv_checksum
1538 * Compute the voucher sum. This is more position-
1539 * relevant than many other checksums - important for
1540 * vouchers (arrays of low, oft-reused, indexes).
1542 static inline iv_index_t
1543 iv_checksum(ipc_voucher_t voucher
, boolean_t
*emptyp
)
1547 boolean_t empty
= TRUE
;
1548 if (0 < voucher
->iv_table_size
) {
1549 iv_index_t i
= voucher
->iv_table_size
- 1;
1552 iv_index_t v
= voucher
->iv_table
[i
];
1553 c
= c
<< 3 | c
>> (32 - 3); /* rotate */
1554 c
= ~c
; /* invert */
1556 c
+= v
; /* add in */
1568 * See if the set of values represented by this new voucher
1569 * already exist in another voucher. If so return a reference
1570 * to the existing voucher and deallocate the voucher provided.
1571 * Otherwise, insert this one in the hash and return it.
1573 * A voucher reference is donated on entry.
1575 * A voucher reference (may be different than on entry).
1577 static ipc_voucher_t
1578 iv_dedup(ipc_voucher_t new_iv
)
1585 sum
= iv_checksum(new_iv
, &empty
);
1587 /* If all values are default, that's the empty (NULL) voucher */
1589 iv_dealloc(new_iv
, FALSE
);
1593 hash
= IV_HASH_BUCKET(sum
);
1596 queue_iterate(&ivht_bucket
[hash
], iv
, ipc_voucher_t
, iv_hash_link
) {
1597 assert(iv
->iv_hash
== hash
);
1599 /* if not already deallocating and sums match... */
1600 if ((os_ref_get_count(&iv
->iv_refs
) > 0) && (iv
->iv_sum
== sum
)) {
1603 assert(iv
->iv_table_size
<= new_iv
->iv_table_size
);
1605 /* and common entries match... */
1606 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
1607 if (iv
->iv_table
[i
] != new_iv
->iv_table
[i
]) {
1611 if (i
< iv
->iv_table_size
) {
1615 /* and all extra entries in new one are unused... */
1616 while (i
< new_iv
->iv_table_size
) {
1617 if (new_iv
->iv_table
[i
++] != IV_UNUSED_VALINDEX
) {
1621 if (i
< new_iv
->iv_table_size
) {
1625 /* ... we found a match... */
1627 /* can we get a ref before it hits 0
1629 * This is thread safe. If the reference count is zero before we
1630 * adjust it, no other thread can have a reference to the voucher.
1631 * The dealloc code requires holding the ivht_lock, so
1632 * the voucher cannot be yanked out from under us.
1634 if (!os_ref_retain_try(&iv
->iv_refs
)) {
1640 /* referenced previous, so deallocate the new one */
1641 iv_dealloc(new_iv
, FALSE
);
1646 /* add the new voucher to the hash, and return it */
1647 new_iv
->iv_sum
= sum
;
1648 new_iv
->iv_hash
= hash
;
1649 queue_enter(&ivht_bucket
[hash
], new_iv
, ipc_voucher_t
, iv_hash_link
);
1654 * This code is disabled for KDEBUG_LEVEL_IST and KDEBUG_LEVEL_NONE
1656 #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD)
1657 if (kdebug_enable
& ~KDEBUG_ENABLE_PPT
) {
1658 uintptr_t voucher_addr
= VM_KERNEL_ADDRPERM((uintptr_t)new_iv
);
1659 uintptr_t attr_tracepoints_needed
= 0;
1661 if (ipc_voucher_trace_contents
) {
1663 * voucher_contents sizing is a bit more constrained
1664 * than might be obvious.
1666 * This is typically a uint8_t typed array. However,
1667 * we want to access it as a uintptr_t to efficiently
1668 * copyout the data in tracepoints.
1670 * This constrains the size to uintptr_t bytes, and
1671 * adds a minimimum alignment requirement equivalent
1674 * Further constraining the size is the fact that it
1675 * is copied out 4 uintptr_t chunks at a time. We do
1676 * NOT want to run off the end of the array and copyout
1677 * random stack data.
1679 * So the minimum size is 4 * sizeof(uintptr_t), and
1680 * the minimum alignment is uintptr_t aligned.
1683 #define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
1684 #define PAYLOAD_SIZE 1024
1686 static_assert(PAYLOAD_SIZE
% PAYLOAD_PER_TRACEPOINT
== 0, "size invariant violated");
1688 mach_voucher_attr_raw_recipe_array_size_t payload_size
= PAYLOAD_SIZE
;
1689 uintptr_t payload
[PAYLOAD_SIZE
/ sizeof(uintptr_t)];
1692 kr
= mach_voucher_extract_all_attr_recipes(new_iv
, (mach_voucher_attr_raw_recipe_array_t
)payload
, &payload_size
);
1693 if (KERN_SUCCESS
== kr
) {
1694 attr_tracepoints_needed
= (payload_size
+ PAYLOAD_PER_TRACEPOINT
- 1) / PAYLOAD_PER_TRACEPOINT
;
1697 * To prevent leaking data from the stack, we
1698 * need to zero data to the end of a tracepoint
1701 size_t remainder
= payload_size
% PAYLOAD_PER_TRACEPOINT
;
1703 bzero((uint8_t*)payload
+ payload_size
,
1704 PAYLOAD_PER_TRACEPOINT
- remainder
);
1708 KDBG(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_CREATE
),
1709 voucher_addr
, new_iv
->iv_table_size
, ivht_count
,
1712 uintptr_t index
= 0;
1713 while (attr_tracepoints_needed
--) {
1714 KDBG(MACHDBG_CODE(DBG_MACH_IPC
,
1715 MACH_IPC_VOUCHER_CREATE_ATTR_DATA
), payload
[index
],
1716 payload
[index
+ 1], payload
[index
+ 2],
1717 payload
[index
+ 3]);
1721 KDBG(MACHDBG_CODE(DBG_MACH_IPC
, MACH_IPC_VOUCHER_CREATE
),
1722 voucher_addr
, new_iv
->iv_table_size
, ivht_count
);
1725 #endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
1731 * Routine: ipc_create_mach_voucher
1733 * Create a new mach voucher and initialize it with the
1734 * value(s) created by having the appropriate resource
1735 * managers interpret the supplied recipe commands and
1738 * Nothing locked (may invoke user-space repeatedly).
1739 * Caller holds references on previous vouchers.
1740 * Previous vouchers are passed as voucher indexes.
1743 ipc_create_mach_voucher(
1744 ipc_voucher_attr_raw_recipe_array_t recipes
,
1745 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1746 ipc_voucher_t
*new_voucher
)
1748 ipc_voucher_attr_recipe_t sub_recipe
;
1749 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1750 ipc_voucher_t voucher
;
1751 kern_return_t kr
= KERN_SUCCESS
;
1753 /* if nothing to do ... */
1754 if (0 == recipe_size
) {
1755 *new_voucher
= IV_NULL
;
1756 return KERN_SUCCESS
;
1759 /* allocate a voucher */
1760 voucher
= iv_alloc(ivgt_keys_in_use
);
1761 if (IV_NULL
== voucher
) {
1762 return KERN_RESOURCE_SHORTAGE
;
1765 /* iterate over the recipe items */
1766 while (0 < recipe_size
- recipe_used
) {
1767 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1768 kr
= KERN_INVALID_ARGUMENT
;
1772 /* find the next recipe */
1773 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1774 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1775 kr
= KERN_INVALID_ARGUMENT
;
1778 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1780 kr
= ipc_execute_voucher_recipe_command(voucher
,
1782 sub_recipe
->command
,
1783 sub_recipe
->previous_voucher
,
1784 sub_recipe
->content
,
1785 sub_recipe
->content_size
,
1787 if (KERN_SUCCESS
!= kr
) {
1792 if (KERN_SUCCESS
== kr
) {
1793 *new_voucher
= iv_dedup(voucher
);
1795 iv_dealloc(voucher
, FALSE
);
1796 *new_voucher
= IV_NULL
;
1802 * Routine: ipc_voucher_attr_control_create_mach_voucher
1804 * Create a new mach voucher and initialize it with the
1805 * value(s) created by having the appropriate resource
1806 * managers interpret the supplied recipe commands and
1809 * The resource manager control's privilege over its
1810 * particular key value is reflected on to the execution
1811 * code, allowing internal commands (like setting a
1812 * key value handle directly, rather than having to
1813 * create a recipe, that will generate a callback just
1817 * Nothing locked (may invoke user-space repeatedly).
1818 * Caller holds references on previous vouchers.
1819 * Previous vouchers are passed as voucher indexes.
1822 ipc_voucher_attr_control_create_mach_voucher(
1823 ipc_voucher_attr_control_t control
,
1824 ipc_voucher_attr_raw_recipe_array_t recipes
,
1825 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1826 ipc_voucher_t
*new_voucher
)
1828 mach_voucher_attr_key_t control_key
;
1829 ipc_voucher_attr_recipe_t sub_recipe
;
1830 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1831 ipc_voucher_t voucher
= IV_NULL
;
1832 kern_return_t kr
= KERN_SUCCESS
;
1834 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
1835 return KERN_INVALID_CAPABILITY
;
1838 /* if nothing to do ... */
1839 if (0 == recipe_size
) {
1840 *new_voucher
= IV_NULL
;
1841 return KERN_SUCCESS
;
1844 /* allocate new voucher */
1845 voucher
= iv_alloc(ivgt_keys_in_use
);
1846 if (IV_NULL
== voucher
) {
1847 return KERN_RESOURCE_SHORTAGE
;
1850 control_key
= iv_index_to_key(control
->ivac_key_index
);
1852 /* iterate over the recipe items */
1853 while (0 < recipe_size
- recipe_used
) {
1854 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1855 kr
= KERN_INVALID_ARGUMENT
;
1859 /* find the next recipe */
1860 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1861 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1862 kr
= KERN_INVALID_ARGUMENT
;
1865 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1867 kr
= ipc_execute_voucher_recipe_command(voucher
,
1869 sub_recipe
->command
,
1870 sub_recipe
->previous_voucher
,
1871 sub_recipe
->content
,
1872 sub_recipe
->content_size
,
1873 (sub_recipe
->key
== control_key
));
1874 if (KERN_SUCCESS
!= kr
) {
1879 if (KERN_SUCCESS
== kr
) {
1880 *new_voucher
= iv_dedup(voucher
);
1882 *new_voucher
= IV_NULL
;
1883 iv_dealloc(voucher
, FALSE
);
1889 * ipc_register_well_known_mach_voucher_attr_manager
1891 * Register the resource manager responsible for a given key value.
1894 ipc_register_well_known_mach_voucher_attr_manager(
1895 ipc_voucher_attr_manager_t manager
,
1896 mach_voucher_attr_value_handle_t default_value
,
1897 mach_voucher_attr_key_t key
,
1898 ipc_voucher_attr_control_t
*control
)
1900 ipc_voucher_attr_control_t new_control
;
1901 iv_index_t key_index
;
1902 iv_index_t hash_index
;
1904 if (IVAM_NULL
== manager
) {
1905 return KERN_INVALID_ARGUMENT
;
1908 key_index
= iv_key_to_index(key
);
1909 if (IV_UNUSED_KEYINDEX
== key_index
) {
1910 return KERN_INVALID_ARGUMENT
;
1913 new_control
= ivac_alloc(key_index
);
1914 if (IVAC_NULL
== new_control
) {
1915 return KERN_RESOURCE_SHORTAGE
;
1918 /* insert the default value into slot 0 */
1919 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_value
= default_value
;
1920 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_refs
= IVACE_REFS_MAX
;
1921 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_made
= IVACE_REFS_MAX
;
1922 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_persist
= TRUE
;
1923 assert(IV_HASH_END
== new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_next
);
1926 if (IVAM_NULL
!= iv_global_table
[key_index
].ivgte_manager
) {
1928 ivac_release(new_control
);
1929 return KERN_INVALID_ARGUMENT
;
1932 /* fill in the global table slot for this key */
1933 iv_global_table
[key_index
].ivgte_manager
= manager
;
1934 iv_global_table
[key_index
].ivgte_control
= new_control
;
1935 iv_global_table
[key_index
].ivgte_key
= key
;
1937 /* insert the default value into the hash (in case it is returned later) */
1938 hash_index
= iv_hash_value(key_index
, default_value
);
1939 assert(IV_HASH_END
== new_control
->ivac_table
[hash_index
].ivace_index
);
1940 new_control
->ivac_table
[hash_index
].ivace_index
= IV_UNUSED_VALINDEX
;
1944 /* return the reference on the new cache control to the caller */
1945 *control
= new_control
;
1947 return KERN_SUCCESS
;
1951 * Routine: mach_voucher_extract_attr_content
1953 * Extract the content for a given <voucher, key> pair.
1955 * If a value other than the default is present for this
1956 * <voucher,key> pair, we need to contact the resource
1957 * manager to extract the content/meaning of the value(s)
1958 * present. Otherwise, return success (but no data).
1961 * Nothing locked - as it may upcall to user-space.
1962 * The caller holds a reference on the voucher.
1965 mach_voucher_extract_attr_content(
1966 ipc_voucher_t voucher
,
1967 mach_voucher_attr_key_t key
,
1968 mach_voucher_attr_content_t content
,
1969 mach_voucher_attr_content_size_t
*in_out_size
)
1971 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1972 mach_voucher_attr_value_handle_array_size_t vals_count
;
1973 mach_voucher_attr_recipe_command_t command
;
1974 ipc_voucher_attr_manager_t manager
;
1975 iv_index_t value_index
;
1976 iv_index_t key_index
;
1980 if (IV_NULL
== voucher
) {
1981 return KERN_INVALID_ARGUMENT
;
1984 key_index
= iv_key_to_index(key
);
1986 value_index
= iv_lookup(voucher
, key_index
);
1987 if (IV_UNUSED_VALINDEX
== value_index
) {
1989 return KERN_SUCCESS
;
1993 * Get the manager for this key_index. The
1994 * existence of a non-default value for this
1995 * slot within our voucher will keep the
1996 * manager referenced during the callout.
1998 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
1999 if (IVAM_NULL
== manager
) {
2000 return KERN_INVALID_ARGUMENT
;
2004 * Get the value(s) to pass to the manager
2005 * for this value_index.
2007 ivace_lookup_values(key_index
, value_index
,
2009 assert(0 < vals_count
);
2011 /* callout to manager */
2013 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2014 vals
, vals_count
, &command
, content
, in_out_size
);
2019 * Routine: mach_voucher_extract_attr_recipe
2021 * Extract a recipe for a given <voucher, key> pair.
2023 * If a value other than the default is present for this
2024 * <voucher,key> pair, we need to contact the resource
2025 * manager to extract the content/meaning of the value(s)
2026 * present. Otherwise, return success (but no data).
2029 * Nothing locked - as it may upcall to user-space.
2030 * The caller holds a reference on the voucher.
2033 mach_voucher_extract_attr_recipe(
2034 ipc_voucher_t voucher
,
2035 mach_voucher_attr_key_t key
,
2036 mach_voucher_attr_raw_recipe_t raw_recipe
,
2037 mach_voucher_attr_raw_recipe_size_t
*in_out_size
)
2039 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2040 mach_voucher_attr_value_handle_array_size_t vals_count
;
2041 ipc_voucher_attr_manager_t manager
;
2042 mach_voucher_attr_recipe_t recipe
;
2043 iv_index_t value_index
;
2044 iv_index_t key_index
;
2048 if (IV_NULL
== voucher
) {
2049 return KERN_INVALID_ARGUMENT
;
2052 key_index
= iv_key_to_index(key
);
2054 value_index
= iv_lookup(voucher
, key_index
);
2055 if (IV_UNUSED_VALINDEX
== value_index
) {
2057 return KERN_SUCCESS
;
2060 if (*in_out_size
< sizeof(*recipe
)) {
2061 return KERN_NO_SPACE
;
2064 recipe
= (mach_voucher_attr_recipe_t
)(void *)raw_recipe
;
2066 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2067 recipe
->previous_voucher
= MACH_VOUCHER_NAME_NULL
;
2068 recipe
->content_size
= *in_out_size
- sizeof(*recipe
);
2071 * Get the manager for this key_index. The
2072 * existence of a non-default value for this
2073 * slot within our voucher will keep the
2074 * manager referenced during the callout.
2076 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2077 if (IVAM_NULL
== manager
) {
2078 return KERN_INVALID_ARGUMENT
;
2082 * Get the value(s) to pass to the manager
2083 * for this value_index.
2085 ivace_lookup_values(key_index
, value_index
,
2087 assert(0 < vals_count
);
2089 /* callout to manager */
2090 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2093 recipe
->content
, &recipe
->content_size
);
2094 if (KERN_SUCCESS
== kr
) {
2095 assert(*in_out_size
- sizeof(*recipe
) >= recipe
->content_size
);
2096 *in_out_size
= sizeof(*recipe
) + recipe
->content_size
;
2105 * Routine: mach_voucher_extract_all_attr_recipes
2107 * Extract all the (non-default) contents for a given voucher,
2108 * building up a recipe that could be provided to a future
2109 * voucher creation call.
2111 * Nothing locked (may invoke user-space).
2112 * Caller holds a reference on the supplied voucher.
2115 mach_voucher_extract_all_attr_recipes(
2116 ipc_voucher_t voucher
,
2117 mach_voucher_attr_raw_recipe_array_t recipes
,
2118 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2120 mach_voucher_attr_recipe_size_t recipe_size
= *in_out_size
;
2121 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2122 iv_index_t key_index
;
2124 if (IV_NULL
== voucher
) {
2125 return KERN_INVALID_ARGUMENT
;
2128 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2129 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2130 mach_voucher_attr_value_handle_array_size_t vals_count
;
2131 mach_voucher_attr_content_size_t content_size
;
2132 ipc_voucher_attr_manager_t manager
;
2133 mach_voucher_attr_recipe_t recipe
;
2134 mach_voucher_attr_key_t key
;
2135 iv_index_t value_index
;
2138 /* don't output anything for a default value */
2139 value_index
= iv_lookup(voucher
, key_index
);
2140 if (IV_UNUSED_VALINDEX
== value_index
) {
2144 if (recipe_size
- recipe_used
< sizeof(*recipe
)) {
2145 return KERN_NO_SPACE
;
2149 * Get the manager for this key_index. The
2150 * existence of a non-default value for this
2151 * slot within our voucher will keep the
2152 * manager referenced during the callout.
2154 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2155 assert(IVAM_NULL
!= manager
);
2156 if (IVAM_NULL
== manager
) {
2160 recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2161 content_size
= recipe_size
- recipe_used
- sizeof(*recipe
);
2164 * Get the value(s) to pass to the manager
2165 * for this value_index.
2167 ivace_lookup_values(key_index
, value_index
,
2169 assert(0 < vals_count
);
2171 key
= iv_index_to_key(key_index
);
2174 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2175 recipe
->content_size
= content_size
;
2177 /* callout to manager */
2178 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2181 recipe
->content
, &recipe
->content_size
);
2182 if (KERN_SUCCESS
!= kr
) {
2186 assert(recipe
->content_size
<= content_size
);
2187 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2190 *in_out_size
= recipe_used
;
2191 return KERN_SUCCESS
;
2195 * Routine: mach_voucher_debug_info
2197 * Extract all the (non-default) contents for a given mach port name,
2198 * building up a recipe that could be provided to a future
2199 * voucher creation call.
2201 * Nothing locked (may invoke user-space).
2202 * Caller may not hold a reference on the supplied voucher.
2204 #if !(DEVELOPMENT || DEBUG)
2206 mach_voucher_debug_info(
2207 ipc_space_t __unused space
,
2208 mach_port_name_t __unused voucher_name
,
2209 mach_voucher_attr_raw_recipe_array_t __unused recipes
,
2210 mach_voucher_attr_raw_recipe_array_size_t __unused
*in_out_size
)
2212 return KERN_NOT_SUPPORTED
;
2216 mach_voucher_debug_info(
2218 mach_port_name_t voucher_name
,
2219 mach_voucher_attr_raw_recipe_array_t recipes
,
2220 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2222 ipc_voucher_t voucher
= IPC_VOUCHER_NULL
;
2224 ipc_port_t port
= MACH_PORT_NULL
;
2226 if (space
== IS_NULL
) {
2227 return KERN_INVALID_TASK
;
2230 if (!MACH_PORT_VALID(voucher_name
)) {
2231 return KERN_INVALID_ARGUMENT
;
2234 kr
= ipc_port_translate_send(space
, voucher_name
, &port
);
2235 if (KERN_SUCCESS
!= kr
) {
2236 return KERN_INVALID_ARGUMENT
;
2239 voucher
= convert_port_to_voucher(port
);
2243 kr
= mach_voucher_extract_all_attr_recipes(voucher
, recipes
, in_out_size
);
2244 ipc_voucher_release(voucher
);
2248 return KERN_FAILURE
;
2253 * Routine: mach_voucher_attr_command
2255 * Invoke an attribute-specific command through this voucher.
2257 * The voucher layout, membership, etc... is not altered
2258 * through the execution of this command.
2261 * Nothing locked - as it may upcall to user-space.
2262 * The caller holds a reference on the voucher.
2265 mach_voucher_attr_command(
2266 ipc_voucher_t voucher
,
2267 mach_voucher_attr_key_t key
,
2268 mach_voucher_attr_command_t command
,
2269 mach_voucher_attr_content_t in_content
,
2270 mach_voucher_attr_content_size_t in_content_size
,
2271 mach_voucher_attr_content_t out_content
,
2272 mach_voucher_attr_content_size_t
*out_content_size
)
2274 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2275 mach_voucher_attr_value_handle_array_size_t vals_count
;
2276 ipc_voucher_attr_manager_t manager
;
2277 ipc_voucher_attr_control_t control
;
2278 iv_index_t value_index
;
2279 iv_index_t key_index
;
2283 if (IV_NULL
== voucher
) {
2284 return KERN_INVALID_ARGUMENT
;
2287 key_index
= iv_key_to_index(key
);
2290 * Get the manager for this key_index.
2291 * Allowing commands against the default value
2292 * for an attribute means that we have to hold
2293 * reference on the attribute manager control
2294 * to keep the manager around during the command
2297 ivgt_lookup(key_index
, TRUE
, &manager
, &control
);
2298 if (IVAM_NULL
== manager
) {
2299 return KERN_INVALID_ARGUMENT
;
2303 * Get the values for this <voucher, key> pair
2304 * to pass to the attribute manager. It is still
2305 * permissible to execute a command against the
2306 * default value (empty value array).
2308 value_index
= iv_lookup(voucher
, key_index
);
2309 ivace_lookup_values(key_index
, value_index
,
2312 /* callout to manager */
2313 kr
= (manager
->ivam_command
)(manager
, key
,
2316 in_content
, in_content_size
,
2317 out_content
, out_content_size
);
2319 /* release reference on control */
2320 ivac_release(control
);
2326 * Routine: mach_voucher_attr_control_get_values
2328 * For a given voucher, get the value handle associated with the
2329 * specified attribute manager.
2332 mach_voucher_attr_control_get_values(
2333 ipc_voucher_attr_control_t control
,
2334 ipc_voucher_t voucher
,
2335 mach_voucher_attr_value_handle_array_t out_values
,
2336 mach_voucher_attr_value_handle_array_size_t
*in_out_size
)
2338 iv_index_t key_index
, value_index
;
2340 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
2341 return KERN_INVALID_CAPABILITY
;
2344 if (IV_NULL
== voucher
) {
2345 return KERN_INVALID_ARGUMENT
;
2348 if (0 == *in_out_size
) {
2349 return KERN_SUCCESS
;
2352 key_index
= control
->ivac_key_index
;
2354 assert(os_ref_get_count(&voucher
->iv_refs
) > 0);
2355 value_index
= iv_lookup(voucher
, key_index
);
2356 ivace_lookup_values(key_index
, value_index
,
2357 out_values
, in_out_size
);
2358 return KERN_SUCCESS
;
2362 * Routine: mach_voucher_attr_control_create_mach_voucher
2364 * Create a new mach voucher and initialize it by processing the
2365 * supplied recipe(s).
2367 * Coming in on the attribute control port denotes special privileges
2368 * over they key associated with the control port.
2370 * Coming in from user-space, each recipe item will have a previous
2371 * recipe port name that needs to be converted to a voucher. Because
2372 * we can't rely on the port namespace to hold a reference on each
2373 * previous voucher port for the duration of processing that command,
2374 * we have to convert the name to a voucher reference and release it
2375 * after the command processing is done.
2378 mach_voucher_attr_control_create_mach_voucher(
2379 ipc_voucher_attr_control_t control
,
2380 mach_voucher_attr_raw_recipe_array_t recipes
,
2381 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2382 ipc_voucher_t
*new_voucher
)
2384 mach_voucher_attr_key_t control_key
;
2385 mach_voucher_attr_recipe_t sub_recipe
;
2386 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2387 ipc_voucher_t voucher
= IV_NULL
;
2388 kern_return_t kr
= KERN_SUCCESS
;
2390 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
) {
2391 return KERN_INVALID_CAPABILITY
;
2394 /* if nothing to do ... */
2395 if (0 == recipe_size
) {
2396 *new_voucher
= IV_NULL
;
2397 return KERN_SUCCESS
;
2400 /* allocate new voucher */
2401 voucher
= iv_alloc(ivgt_keys_in_use
);
2402 if (IV_NULL
== voucher
) {
2403 return KERN_RESOURCE_SHORTAGE
;
2406 control_key
= iv_index_to_key(control
->ivac_key_index
);
2408 /* iterate over the recipe items */
2409 while (0 < recipe_size
- recipe_used
) {
2410 ipc_voucher_t prev_iv
;
2412 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2413 kr
= KERN_INVALID_ARGUMENT
;
2417 /* find the next recipe */
2418 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2419 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2420 kr
= KERN_INVALID_ARGUMENT
;
2423 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2425 /* convert voucher port name (current space) into a voucher reference */
2426 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2427 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2428 kr
= KERN_INVALID_CAPABILITY
;
2432 kr
= ipc_execute_voucher_recipe_command(voucher
,
2434 sub_recipe
->command
,
2436 sub_recipe
->content
,
2437 sub_recipe
->content_size
,
2438 (sub_recipe
->key
== control_key
));
2439 ipc_voucher_release(prev_iv
);
2441 if (KERN_SUCCESS
!= kr
) {
2446 if (KERN_SUCCESS
== kr
) {
2447 *new_voucher
= iv_dedup(voucher
);
2449 *new_voucher
= IV_NULL
;
2450 iv_dealloc(voucher
, FALSE
);
2456 * Routine: host_create_mach_voucher
2458 * Create a new mach voucher and initialize it by processing the
2459 * supplied recipe(s).
2461 * Comming in from user-space, each recipe item will have a previous
2462 * recipe port name that needs to be converted to a voucher. Because
2463 * we can't rely on the port namespace to hold a reference on each
2464 * previous voucher port for the duration of processing that command,
2465 * we have to convert the name to a voucher reference and release it
2466 * after the command processing is done.
2469 host_create_mach_voucher(
2471 mach_voucher_attr_raw_recipe_array_t recipes
,
2472 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2473 ipc_voucher_t
*new_voucher
)
2475 mach_voucher_attr_recipe_t sub_recipe
;
2476 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2477 ipc_voucher_t voucher
= IV_NULL
;
2478 kern_return_t kr
= KERN_SUCCESS
;
2480 if (host
== HOST_NULL
) {
2481 return KERN_INVALID_ARGUMENT
;
2484 /* if nothing to do ... */
2485 if (0 == recipe_size
) {
2486 *new_voucher
= IV_NULL
;
2487 return KERN_SUCCESS
;
2490 /* allocate new voucher */
2491 voucher
= iv_alloc(ivgt_keys_in_use
);
2492 if (IV_NULL
== voucher
) {
2493 return KERN_RESOURCE_SHORTAGE
;
2496 /* iterate over the recipe items */
2497 while (0 < recipe_size
- recipe_used
) {
2498 ipc_voucher_t prev_iv
;
2500 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2501 kr
= KERN_INVALID_ARGUMENT
;
2505 /* find the next recipe */
2506 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2507 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2508 kr
= KERN_INVALID_ARGUMENT
;
2511 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2513 /* convert voucher port name (current space) into a voucher reference */
2514 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2515 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2516 kr
= KERN_INVALID_CAPABILITY
;
2520 kr
= ipc_execute_voucher_recipe_command(voucher
,
2522 sub_recipe
->command
,
2524 sub_recipe
->content
,
2525 sub_recipe
->content_size
,
2527 ipc_voucher_release(prev_iv
);
2529 if (KERN_SUCCESS
!= kr
) {
2534 if (KERN_SUCCESS
== kr
) {
2535 *new_voucher
= iv_dedup(voucher
);
2537 *new_voucher
= IV_NULL
;
2538 iv_dealloc(voucher
, FALSE
);
2544 * Routine: host_register_well_known_mach_voucher_attr_manager
2546 * Register the user-level resource manager responsible for a given
2549 * The manager port passed in has to be converted/wrapped
2550 * in an ipc_voucher_attr_manager_t structure and then call the
2551 * internal variant. We have a generic ipc voucher manager
2552 * type that implements a MIG proxy out to user-space just for
2556 host_register_well_known_mach_voucher_attr_manager(
2558 mach_voucher_attr_manager_t __unused manager
,
2559 mach_voucher_attr_value_handle_t __unused default_value
,
2560 mach_voucher_attr_key_t __unused key
,
2561 ipc_voucher_attr_control_t __unused
*control
)
2563 if (HOST_NULL
== host
) {
2564 return KERN_INVALID_HOST
;
2568 return KERN_NOT_SUPPORTED
;
2571 * Allocate a mig_voucher_attr_manager_t that provides the
2572 * MIG proxy functions for the three manager callbacks and
2573 * store the port right in there.
2575 * If the user-space manager dies, we'll detect it on our
2576 * next upcall, and cleanup the proxy at that point.
2578 mig_voucher_attr_manager_t proxy
;
2581 proxy
= mvam_alloc(manager
);
2583 kr
= ipc_register_well_known_mach_voucher_attr_manager(&proxy
->mvam_manager
,
2587 if (KERN_SUCCESS
!= kr
) {
2588 mvam_release(proxy
);
2596 * Routine: host_register_mach_voucher_attr_manager
2598 * Register the user-space resource manager and return a
2599 * dynamically allocated key.
2601 * Wrap the supplied port with the MIG proxy ipc
2602 * voucher resource manager, and then call the internal
2606 host_register_mach_voucher_attr_manager(
2608 mach_voucher_attr_manager_t __unused manager
,
2609 mach_voucher_attr_value_handle_t __unused default_value
,
2610 mach_voucher_attr_key_t __unused
*key
,
2611 ipc_voucher_attr_control_t __unused
*control
)
2613 if (HOST_NULL
== host
) {
2614 return KERN_INVALID_HOST
;
2617 return KERN_NOT_SUPPORTED
;
2621 * Routine: ipc_get_pthpriority_from_kmsg_voucher
2623 * Get the canonicalized pthread priority from the voucher attached in the kmsg.
2626 ipc_get_pthpriority_from_kmsg_voucher(
2628 ipc_pthread_priority_value_t
*canonicalize_priority_value
)
2630 ipc_voucher_t pthread_priority_voucher
;
2631 mach_voucher_attr_raw_recipe_size_t content_size
=
2632 sizeof(mach_voucher_attr_recipe_data_t
) + sizeof(ipc_pthread_priority_value_t
);
2633 uint8_t content_data
[content_size
];
2634 mach_voucher_attr_recipe_t cur_content
;
2635 kern_return_t kr
= KERN_SUCCESS
;
2637 if (!IP_VALID(kmsg
->ikm_voucher
)) {
2638 return KERN_FAILURE
;
2641 pthread_priority_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2642 kr
= mach_voucher_extract_attr_recipe(pthread_priority_voucher
,
2643 MACH_VOUCHER_ATTR_KEY_PTHPRIORITY
,
2646 if (kr
!= KERN_SUCCESS
) {
2650 /* return KERN_INVALID_VALUE for default value */
2651 if (content_size
< sizeof(mach_voucher_attr_recipe_t
)) {
2652 return KERN_INVALID_VALUE
;
2655 cur_content
= (mach_voucher_attr_recipe_t
) (void *) &content_data
[0];
2656 assert(cur_content
->content_size
== sizeof(ipc_pthread_priority_value_t
));
2657 memcpy(canonicalize_priority_value
, cur_content
->content
, sizeof(ipc_pthread_priority_value_t
));
2659 return KERN_SUCCESS
;
2664 * Routine: ipc_voucher_send_preprocessing
2666 * Processing of the voucher in the kmsg before sending it.
2667 * Currently use to switch PERSONA_TOKEN in case of process with
2668 * no com.apple.private.personas.propagate entitlement.
2671 ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg
)
2673 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2674 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2675 sizeof(ipc_voucher_attr_recipe_data_t
);
2676 ipc_voucher_t pre_processed_voucher
;
2677 ipc_voucher_t voucher_to_send
;
2679 int need_preprocessing
= FALSE
;
2681 if (!IP_VALID(kmsg
->ikm_voucher
) || current_task() == kernel_task
) {
2685 /* setup recipe for preprocessing of all the attributes. */
2686 pre_processed_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2688 kr
= ipc_voucher_prepare_processing_recipe(pre_processed_voucher
,
2689 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2690 &recipe_size
, MACH_VOUCHER_ATTR_SEND_PREPROCESS
,
2691 IVAM_FLAGS_SUPPORT_SEND_PREPROCESS
, &need_preprocessing
);
2693 assert(KERN_SUCCESS
== kr
);
2695 * Only do send preprocessing if the voucher needs any pre processing.
2697 if (need_preprocessing
) {
2698 kr
= ipc_create_mach_voucher(recipes
,
2701 assert(KERN_SUCCESS
== kr
);
2702 ipc_port_release_send(kmsg
->ikm_voucher
);
2703 kmsg
->ikm_voucher
= convert_voucher_to_port(voucher_to_send
);
2708 * Routine: ipc_voucher_receive_postprocessing
2710 * Redeems the voucher attached to the kmsg.
2712 * Although it is possible to call ipc_importance_receive
2713 * here, it is called in mach_msg_receive_results and not here
2714 * in order to maintain symmetry with ipc_voucher_send_preprocessing.
2717 ipc_voucher_receive_postprocessing(
2719 mach_msg_option_t option
)
2721 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2722 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2723 sizeof(ipc_voucher_attr_recipe_data_t
);
2724 ipc_voucher_t recv_voucher
;
2725 ipc_voucher_t sent_voucher
;
2727 int need_postprocessing
= FALSE
;
2729 if ((option
& MACH_RCV_VOUCHER
) == 0 || (!IP_VALID(kmsg
->ikm_voucher
)) ||
2730 current_task() == kernel_task
) {
2734 /* setup recipe for auto redeem of all the attributes. */
2735 sent_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2737 kr
= ipc_voucher_prepare_processing_recipe(sent_voucher
,
2738 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2739 &recipe_size
, MACH_VOUCHER_ATTR_AUTO_REDEEM
,
2740 IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS
, &need_postprocessing
);
2742 assert(KERN_SUCCESS
== kr
);
2745 * Only do receive postprocessing if the voucher needs any post processing.
2747 if (need_postprocessing
) {
2748 kr
= ipc_create_mach_voucher(recipes
,
2751 assert(KERN_SUCCESS
== kr
);
2752 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
2753 kmsg
->ikm_header
->msgh_bits
|= (MACH_MSG_TYPE_MOVE_SEND
<< 16);
2754 ipc_port_release_send(kmsg
->ikm_voucher
);
2755 kmsg
->ikm_voucher
= convert_voucher_to_port(recv_voucher
);
2760 * Routine: ipc_voucher_prepare_processing_recipe
2762 * Check if the given voucher has an attribute which supports
2763 * the given flag and prepare a recipe to apply that supported
2766 static kern_return_t
2767 ipc_voucher_prepare_processing_recipe(
2768 ipc_voucher_t voucher
,
2769 ipc_voucher_attr_raw_recipe_array_t recipes
,
2770 ipc_voucher_attr_raw_recipe_array_size_t
*in_out_size
,
2771 mach_voucher_attr_recipe_command_t command
,
2772 ipc_voucher_attr_manager_flags flags
,
2773 int *need_processing
)
2775 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= *in_out_size
;
2776 ipc_voucher_attr_raw_recipe_array_size_t recipe_used
= 0;
2777 iv_index_t key_index
;
2778 ipc_voucher_attr_recipe_t recipe
;
2780 if (IV_NULL
== voucher
) {
2781 return KERN_INVALID_ARGUMENT
;
2784 /* Setup a recipe to copy all attributes. */
2785 if (recipe_size
< sizeof(*recipe
)) {
2786 return KERN_NO_SPACE
;
2789 *need_processing
= FALSE
;
2790 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2791 recipe
->key
= MACH_VOUCHER_ATTR_KEY_ALL
;
2792 recipe
->command
= MACH_VOUCHER_ATTR_COPY
;
2793 recipe
->previous_voucher
= voucher
;
2794 recipe
->content_size
= 0;
2795 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2797 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2798 ipc_voucher_attr_manager_t manager
;
2799 mach_voucher_attr_key_t key
;
2800 iv_index_t value_index
;
2802 /* don't output anything for a default value */
2803 value_index
= iv_lookup(voucher
, key_index
);
2804 if (IV_UNUSED_VALINDEX
== value_index
) {
2808 if (recipe_size
- recipe_used
< sizeof(*recipe
)) {
2809 return KERN_NO_SPACE
;
2812 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2815 * Get the manager for this key_index. The
2816 * existence of a non-default value for this
2817 * slot within our voucher will keep the
2818 * manager referenced during the callout.
2820 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2821 assert(IVAM_NULL
!= manager
);
2822 if (IVAM_NULL
== manager
) {
2826 /* Check if the supported flag is set in the manager */
2827 if ((manager
->ivam_flags
& flags
) == 0) {
2831 key
= iv_index_to_key(key_index
);
2834 recipe
->command
= command
;
2835 recipe
->content_size
= 0;
2836 recipe
->previous_voucher
= voucher
;
2838 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2839 *need_processing
= TRUE
;
2842 *in_out_size
= recipe_used
;
2843 return KERN_SUCCESS
;
2847 * Activity id Generation
2849 uint64_t voucher_activity_id
;
2851 #define generate_activity_id(x) \
2852 ((uint64_t)OSAddAtomic64((x), (int64_t *)&voucher_activity_id))
2855 * Routine: mach_init_activity_id
2857 * Initialize voucher activity id.
2860 mach_init_activity_id(void)
2862 voucher_activity_id
= 1;
2866 * Routine: mach_generate_activity_id
2868 * Generate a system wide voucher activity id.
2871 mach_generate_activity_id(
2872 struct mach_generate_activity_id_args
*args
)
2874 uint64_t activity_id
;
2875 kern_return_t kr
= KERN_SUCCESS
;
2877 if (args
->count
<= 0 || args
->count
> MACH_ACTIVITY_ID_COUNT_MAX
) {
2878 return KERN_INVALID_ARGUMENT
;
2881 activity_id
= generate_activity_id(args
->count
);
2882 kr
= copyout(&activity_id
, args
->activity_id
, sizeof(activity_id
));
2887 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2890 * Build-in a simple User Data Resource Manager
2892 #define USER_DATA_MAX_DATA (16*1024)
2894 struct user_data_value_element
{
2895 mach_voucher_attr_value_reference_t e_made
;
2896 mach_voucher_attr_content_size_t e_size
;
2899 queue_chain_t e_hash_link
;
2903 typedef struct user_data_value_element
*user_data_element_t
;
2906 * User Data Voucher Hash Table
2908 #define USER_DATA_HASH_BUCKETS 127
2909 #define USER_DATA_HASH_BUCKET(x) ((x) % USER_DATA_HASH_BUCKETS)
2911 static queue_head_t user_data_bucket
[USER_DATA_HASH_BUCKETS
];
2912 static lck_spin_t user_data_lock_data
;
2914 #define user_data_lock_init() \
2915 lck_spin_init(&user_data_lock_data, &ipc_lck_grp, &ipc_lck_attr)
2916 #define user_data_lock_destroy() \
2917 lck_spin_destroy(&user_data_lock_data, &ipc_lck_grp)
2918 #define user_data_lock() \
2919 lck_spin_lock_grp(&user_data_lock_data, &ipc_lck_grp)
2920 #define user_data_lock_try() \
2921 lck_spin_try_lock_grp(&user_data_lock_data, &ipc_lck_grp)
2922 #define user_data_unlock() \
2923 lck_spin_unlock(&user_data_lock_data)
2925 static kern_return_t
2926 user_data_release_value(
2927 ipc_voucher_attr_manager_t manager
,
2928 mach_voucher_attr_key_t key
,
2929 mach_voucher_attr_value_handle_t value
,
2930 mach_voucher_attr_value_reference_t sync
);
2932 static kern_return_t
2933 user_data_get_value(
2934 ipc_voucher_attr_manager_t manager
,
2935 mach_voucher_attr_key_t key
,
2936 mach_voucher_attr_recipe_command_t command
,
2937 mach_voucher_attr_value_handle_array_t prev_values
,
2938 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
2939 mach_voucher_attr_content_t content
,
2940 mach_voucher_attr_content_size_t content_size
,
2941 mach_voucher_attr_value_handle_t
*out_value
,
2942 mach_voucher_attr_value_flags_t
*out_flags
,
2943 ipc_voucher_t
*out_value_voucher
);
2945 static kern_return_t
2946 user_data_extract_content(
2947 ipc_voucher_attr_manager_t manager
,
2948 mach_voucher_attr_key_t key
,
2949 mach_voucher_attr_value_handle_array_t values
,
2950 mach_voucher_attr_value_handle_array_size_t value_count
,
2951 mach_voucher_attr_recipe_command_t
*out_command
,
2952 mach_voucher_attr_content_t out_content
,
2953 mach_voucher_attr_content_size_t
*in_out_content_size
);
2955 static kern_return_t
2957 ipc_voucher_attr_manager_t manager
,
2958 mach_voucher_attr_key_t key
,
2959 mach_voucher_attr_value_handle_array_t values
,
2960 mach_msg_type_number_t value_count
,
2961 mach_voucher_attr_command_t command
,
2962 mach_voucher_attr_content_t in_content
,
2963 mach_voucher_attr_content_size_t in_content_size
,
2964 mach_voucher_attr_content_t out_content
,
2965 mach_voucher_attr_content_size_t
*out_content_size
);
2969 ipc_voucher_attr_manager_t manager
);
2971 const struct ipc_voucher_attr_manager user_data_manager
= {
2972 .ivam_release_value
= user_data_release_value
,
2973 .ivam_get_value
= user_data_get_value
,
2974 .ivam_extract_content
= user_data_extract_content
,
2975 .ivam_command
= user_data_command
,
2976 .ivam_release
= user_data_release
,
2977 .ivam_flags
= IVAM_FLAGS_NONE
,
2980 ipc_voucher_attr_control_t user_data_control
;
2981 ipc_voucher_attr_control_t test_control
;
2983 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) && defined(MACH_VOUCHER_ATTR_KEY_TEST)
2984 #define USER_DATA_ASSERT_KEY(key) \
2985 assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key) || \
2986 MACH_VOUCHER_ATTR_KEY_TEST == (key));
2987 #elif defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
2988 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key))
2990 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_TEST == (key))
2994 * Routine: user_data_release_value
2996 * Release a made reference on a specific value managed by
2997 * this voucher attribute manager.
2999 * Must remove the element associated with this value from
3000 * the hash if this is the last know made reference.
3002 static kern_return_t
3003 user_data_release_value(
3004 ipc_voucher_attr_manager_t __assert_only manager
,
3005 mach_voucher_attr_key_t __assert_only key
,
3006 mach_voucher_attr_value_handle_t value
,
3007 mach_voucher_attr_value_reference_t sync
)
3009 user_data_element_t elem
;
3012 assert(&user_data_manager
== manager
);
3013 USER_DATA_ASSERT_KEY(key
);
3015 elem
= (user_data_element_t
)value
;
3016 hash
= elem
->e_hash
;
3019 if (sync
== elem
->e_made
) {
3020 queue_remove(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
);
3022 kfree(elem
, sizeof(*elem
) + elem
->e_size
);
3023 return KERN_SUCCESS
;
3025 assert(sync
< elem
->e_made
);
3028 return KERN_FAILURE
;
3032 * Routine: user_data_checksum
3034 * Provide a rudimentary checksum for the data presented
3035 * to these voucher attribute managers.
3039 mach_voucher_attr_content_t content
,
3040 mach_voucher_attr_content_size_t content_size
)
3042 mach_voucher_attr_content_size_t i
;
3043 iv_index_t cksum
= 0;
3045 for (i
= 0; i
< content_size
; i
++, content
++) {
3046 cksum
= (cksum
<< 8) ^ (cksum
+ *(unsigned char *)content
);
3053 * Routine: user_data_dedup
3055 * See if the content represented by this request already exists
3056 * in another user data element. If so return a made reference
3057 * to the existing element. Otherwise, create a new element and
3058 * return that (after inserting it in the hash).
3062 * A made reference on the user_data_element_t
3064 static user_data_element_t
3066 mach_voucher_attr_content_t content
,
3067 mach_voucher_attr_content_size_t content_size
)
3071 user_data_element_t elem
;
3072 user_data_element_t alloc
= NULL
;
3074 sum
= user_data_checksum(content
, content_size
);
3075 hash
= USER_DATA_HASH_BUCKET(sum
);
3079 queue_iterate(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
) {
3080 assert(elem
->e_hash
== hash
);
3082 /* if sums match... */
3083 if (elem
->e_sum
== sum
&& elem
->e_size
== content_size
) {
3086 /* and all data matches */
3087 for (i
= 0; i
< content_size
; i
++) {
3088 if (elem
->e_data
[i
] != content
[i
]) {
3092 if (i
< content_size
) {
3096 /* ... we found a match... */
3101 if (NULL
!= alloc
) {
3102 kfree(alloc
, sizeof(*alloc
) + content_size
);
3109 if (NULL
== alloc
) {
3112 alloc
= (user_data_element_t
)kalloc(sizeof(*alloc
) + content_size
);
3114 alloc
->e_size
= content_size
;
3116 alloc
->e_hash
= hash
;
3117 memcpy(alloc
->e_data
, content
, content_size
);
3121 queue_enter(&user_data_bucket
[hash
], alloc
, user_data_element_t
, e_hash_link
);
3127 static kern_return_t
3128 user_data_get_value(
3129 ipc_voucher_attr_manager_t __assert_only manager
,
3130 mach_voucher_attr_key_t __assert_only key
,
3131 mach_voucher_attr_recipe_command_t command
,
3132 mach_voucher_attr_value_handle_array_t prev_values
,
3133 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3134 mach_voucher_attr_content_t content
,
3135 mach_voucher_attr_content_size_t content_size
,
3136 mach_voucher_attr_value_handle_t
*out_value
,
3137 mach_voucher_attr_value_flags_t
*out_flags
,
3138 ipc_voucher_t
*out_value_voucher
)
3140 user_data_element_t elem
;
3142 assert(&user_data_manager
== manager
);
3143 USER_DATA_ASSERT_KEY(key
);
3145 /* never an out voucher */
3146 *out_value_voucher
= IPC_VOUCHER_NULL
;
3147 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
3150 case MACH_VOUCHER_ATTR_REDEEM
:
3152 /* redeem of previous values is the value */
3153 if (0 < prev_value_count
) {
3154 elem
= (user_data_element_t
)prev_values
[0];
3155 assert(0 < elem
->e_made
);
3157 *out_value
= prev_values
[0];
3158 return KERN_SUCCESS
;
3161 /* redeem of default is default */
3163 return KERN_SUCCESS
;
3165 case MACH_VOUCHER_ATTR_USER_DATA_STORE
:
3166 if (USER_DATA_MAX_DATA
< content_size
) {
3167 return KERN_RESOURCE_SHORTAGE
;
3170 /* empty is the default */
3171 if (0 == content_size
) {
3173 return KERN_SUCCESS
;
3176 elem
= user_data_dedup(content
, content_size
);
3177 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
3178 return KERN_SUCCESS
;
3181 /* every other command is unknown */
3182 return KERN_INVALID_ARGUMENT
;
3186 static kern_return_t
3187 user_data_extract_content(
3188 ipc_voucher_attr_manager_t __assert_only manager
,
3189 mach_voucher_attr_key_t __assert_only key
,
3190 mach_voucher_attr_value_handle_array_t values
,
3191 mach_voucher_attr_value_handle_array_size_t value_count
,
3192 mach_voucher_attr_recipe_command_t
*out_command
,
3193 mach_voucher_attr_content_t out_content
,
3194 mach_voucher_attr_content_size_t
*in_out_content_size
)
3196 mach_voucher_attr_content_size_t size
= 0;
3197 user_data_element_t elem
;
3200 assert(&user_data_manager
== manager
);
3201 USER_DATA_ASSERT_KEY(key
);
3203 /* concatenate the stored data items */
3204 for (i
= 0; i
< value_count
&& *in_out_content_size
> 0; i
++) {
3205 elem
= (user_data_element_t
)values
[i
];
3206 assert(USER_DATA_MAX_DATA
>= elem
->e_size
);
3208 if (size
+ elem
->e_size
> *in_out_content_size
) {
3209 return KERN_NO_SPACE
;
3212 memcpy(&out_content
[size
], elem
->e_data
, elem
->e_size
);
3213 size
+= elem
->e_size
;
3215 *out_command
= MACH_VOUCHER_ATTR_BITS_STORE
;
3216 *in_out_content_size
= size
;
3217 return KERN_SUCCESS
;
3220 static kern_return_t
3222 ipc_voucher_attr_manager_t __assert_only manager
,
3223 mach_voucher_attr_key_t __assert_only key
,
3224 mach_voucher_attr_value_handle_array_t __unused values
,
3225 mach_msg_type_number_t __unused value_count
,
3226 mach_voucher_attr_command_t __unused command
,
3227 mach_voucher_attr_content_t __unused in_content
,
3228 mach_voucher_attr_content_size_t __unused in_content_size
,
3229 mach_voucher_attr_content_t __unused out_content
,
3230 mach_voucher_attr_content_size_t __unused
*out_content_size
)
3232 assert(&user_data_manager
== manager
);
3233 USER_DATA_ASSERT_KEY(key
);
3234 return KERN_FAILURE
;
3239 ipc_voucher_attr_manager_t manager
)
3241 if (manager
!= &user_data_manager
) {
3245 panic("Voucher user-data manager released");
3248 static int user_data_manager_inited
= 0;
3251 user_data_attr_manager_init()
3255 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
3256 if ((user_data_manager_inited
& 0x1) != 0x1) {
3257 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3258 (mach_voucher_attr_value_handle_t
)0,
3259 MACH_VOUCHER_ATTR_KEY_USER_DATA
,
3260 &user_data_control
);
3261 if (KERN_SUCCESS
!= kr
) {
3262 printf("Voucher user-data manager register(USER-DATA) returned %d", kr
);
3264 user_data_manager_inited
|= 0x1;
3268 #if defined(MACH_VOUCHER_ATTR_KEY_TEST)
3269 if ((user_data_manager_inited
& 0x2) != 0x2) {
3270 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3271 (mach_voucher_attr_value_handle_t
)0,
3272 MACH_VOUCHER_ATTR_KEY_TEST
,
3274 if (KERN_SUCCESS
!= kr
) {
3275 printf("Voucher user-data manager register(TEST) returned %d", kr
);
3277 user_data_manager_inited
|= 0x2;
3281 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
3284 for (i
= 0; i
< USER_DATA_HASH_BUCKETS
; i
++) {
3285 queue_init(&user_data_bucket
[i
]);
3288 user_data_lock_init();
3292 #endif /* MACH_DEBUG */