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(&ivht_lock_data)
72 #define ivht_lock_try() \
73 lck_spin_try_lock(&ivht_lock_data)
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(&ivgt_lock_data)
94 #define ivgt_lock_try() \
95 lck_spin_try_lock(&ivgt_lock_data)
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
);
101 extern int thread_qos_from_pthread_priority(unsigned long, unsigned long *);
103 static inline iv_refs_t
104 iv_reference(ipc_voucher_t iv
)
108 refs
= hw_atomic_add(&iv
->iv_refs
, 1);
113 iv_release(ipc_voucher_t iv
)
117 assert(0 < iv
->iv_refs
);
118 refs
= hw_atomic_sub(&iv
->iv_refs
, 1);
120 iv_dealloc(iv
, TRUE
);
124 * freelist helper macros
126 #define IV_FREELIST_END ((iv_index_t) 0)
129 * Attribute value hashing helper macros
131 #define IV_HASH_END UINT32_MAX
132 #define IV_HASH_VAL(sz, val) \
133 (((val) >> 3) % (sz))
135 static inline iv_index_t
137 iv_index_t key_index
,
138 mach_voucher_attr_value_handle_t value
)
140 ipc_voucher_attr_control_t ivac
;
142 ivac
= iv_global_table
[key_index
].ivgte_control
;
143 assert(IVAC_NULL
!= ivac
);
144 return IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
148 * Convert a key to an index. This key-index is used to both index
149 * into the voucher table of attribute cache indexes and also the
150 * table of resource managers by key.
152 * For now, well-known keys have a one-to-one mapping of indexes
153 * into these tables. But as time goes on, that may not always
154 * be the case (sparse use over time). This isolates the code from
155 * having to change in these cases - yet still lets us keep a densely
156 * packed set of tables.
158 static inline iv_index_t
159 iv_key_to_index(mach_voucher_attr_key_t key
)
161 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
||
162 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
< key
)
163 return IV_UNUSED_KEYINDEX
;
164 return (iv_index_t
)key
- 1;
167 static inline mach_voucher_attr_key_t
168 iv_index_to_key(iv_index_t key_index
)
170 if (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
> key_index
)
171 return iv_global_table
[key_index
].ivgte_key
;
172 return MACH_VOUCHER_ATTR_KEY_NONE
;
176 static void ivace_release(iv_index_t key_index
, iv_index_t value_index
);
177 static void ivace_lookup_values(iv_index_t key_index
, iv_index_t value_index
,
178 mach_voucher_attr_value_handle_array_t values
,
179 mach_voucher_attr_value_handle_array_size_t
*count
);
181 static iv_index_t
iv_lookup(ipc_voucher_t
, iv_index_t
);
184 static void ivgt_lookup(iv_index_t
,
186 ipc_voucher_attr_manager_t
*,
187 ipc_voucher_attr_control_t
*);
190 ipc_voucher_prepare_processing_recipe(
191 ipc_voucher_t voucher
,
192 ipc_voucher_attr_raw_recipe_array_t recipes
,
193 ipc_voucher_attr_raw_recipe_array_size_t
*in_out_size
,
194 mach_voucher_attr_recipe_command_t command
,
195 ipc_voucher_attr_manager_flags flags
,
196 int *need_processing
);
198 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
199 void user_data_attr_manager_init(void);
203 ipc_voucher_init(void)
205 natural_t ipc_voucher_max
= (task_max
+ thread_max
) * 2;
206 natural_t attr_manager_max
= MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
;
209 ipc_voucher_zone
= zinit(sizeof(struct ipc_voucher
),
210 ipc_voucher_max
* sizeof(struct ipc_voucher
),
211 sizeof(struct ipc_voucher
),
213 zone_change(ipc_voucher_zone
, Z_NOENCRYPT
, TRUE
);
215 ipc_voucher_attr_control_zone
= zinit(sizeof(struct ipc_voucher_attr_control
),
216 attr_manager_max
* sizeof(struct ipc_voucher_attr_control
),
217 sizeof(struct ipc_voucher_attr_control
),
218 "ipc voucher attr controls");
219 zone_change(ipc_voucher_attr_control_zone
, Z_NOENCRYPT
, TRUE
);
221 /* initialize voucher hash */
223 for (i
= 0; i
< IV_HASH_BUCKETS
; i
++)
224 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
);
248 iv
->iv_port
= IP_NULL
;
250 if (entries
> IV_ENTRIES_INLINE
) {
253 /* TODO - switch to ipc_table method of allocation */
254 table
= (iv_entry_t
) kalloc(sizeof(*table
) * entries
);
255 if (IVE_NULL
== table
) {
256 zfree(ipc_voucher_zone
, iv
);
259 iv
->iv_table
= table
;
260 iv
->iv_table_size
= entries
;
262 iv
->iv_table
= iv
->iv_inline_table
;
263 iv
->iv_table_size
= IV_ENTRIES_INLINE
;
266 /* initialize the table entries */
267 for (i
=0; i
< iv
->iv_table_size
; i
++)
268 iv
->iv_table
[i
] = IV_UNUSED_VALINDEX
;
276 * Set the voucher's value index for a given key index.
278 * This is only called during voucher creation, as
279 * they are immutable once references are distributed.
282 iv_set(ipc_voucher_t iv
,
283 iv_index_t key_index
,
284 iv_index_t value_index
)
286 assert(key_index
< iv
->iv_table_size
);
287 iv
->iv_table
[key_index
] = value_index
;
291 iv_dealloc(ipc_voucher_t iv
, boolean_t unhash
)
293 ipc_port_t port
= iv
->iv_port
;
297 * Do we have to remove it from the hash?
301 assert(0 == iv
->iv_refs
);
302 assert(IV_HASH_BUCKETS
> iv
->iv_hash
);
303 queue_remove(&ivht_bucket
[iv
->iv_hash
], iv
, ipc_voucher_t
, iv_hash_link
);
307 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_DESTROY
) | DBG_FUNC_NONE
,
308 VM_KERNEL_ADDRPERM((uintptr_t)iv
), 0, ivht_count
, 0, 0);
311 assert(0 == --iv
->iv_refs
);
314 * if a port was allocated for this voucher,
315 * it must not have any remaining send rights,
316 * because the port's reference on the voucher
317 * is gone. We can just discard it now.
319 if (IP_VALID(port
)) {
320 assert(ip_active(port
));
321 assert(port
->ip_srights
== 0);
323 ipc_port_dealloc_kernel(port
);
326 /* release the attribute references held by this voucher */
327 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
328 ivace_release(i
, iv
->iv_table
[i
]);
334 if (iv
->iv_table
!= iv
->iv_inline_table
)
336 iv
->iv_table_size
* sizeof(*iv
->iv_table
));
338 zfree(ipc_voucher_zone
, iv
);
344 * Find the voucher's value index for a given key_index
346 * Vouchers are immutable, so no locking required to do
349 static inline iv_index_t
350 iv_lookup(ipc_voucher_t iv
, iv_index_t key_index
)
352 if (key_index
< iv
->iv_table_size
)
353 return iv
->iv_table
[key_index
];
354 return IV_UNUSED_VALINDEX
;
358 * Routine: unsafe_convert_port_to_voucher
360 * Unsafe conversion of a port to a voucher.
361 * Intended only for use by trace and debugging
362 * code. Consumes nothing, validates very little,
363 * produces an unreferenced voucher, which you
364 * MAY NOT use as a voucher, only log as an
367 * Caller has a send-right reference to port.
368 * Port may or may not be locked.
371 unsafe_convert_port_to_voucher(
374 if (IP_VALID(port
)) {
375 uintptr_t voucher
= (uintptr_t) port
->ip_kobject
;
378 * No need to lock because we have a reference on the
379 * port, and if it is a true voucher port, that reference
380 * keeps the voucher bound to the port (and active).
382 if (ip_kotype(port
) == IKOT_VOUCHER
)
385 return (uintptr_t)IV_NULL
;
389 * Routine: convert_port_to_voucher
391 * Convert from a port to a voucher.
392 * Doesn't consume the port [send-right] ref;
393 * produces a voucher ref, which may be null.
395 * Caller has a send-right reference to port.
396 * Port may or may not be locked.
399 convert_port_to_voucher(
402 if (IP_VALID(port
)) {
403 ipc_voucher_t voucher
= (ipc_voucher_t
) port
->ip_kobject
;
406 * No need to lock because we have a reference on the
407 * port, and if it is a true voucher port, that reference
408 * keeps the voucher bound to the port (and active).
410 if (ip_kotype(port
) != IKOT_VOUCHER
)
413 assert(ip_active(port
));
415 ipc_voucher_reference(voucher
);
422 * Routine: convert_port_name_to_voucher
424 * Convert from a port name in the current space to a voucher.
425 * Produces a voucher ref, which may be null.
431 convert_port_name_to_voucher(
432 mach_port_name_t voucher_name
)
438 if (MACH_PORT_VALID(voucher_name
)) {
439 kr
= ipc_port_translate_send(current_space(), voucher_name
, &port
);
440 if (KERN_SUCCESS
!= kr
)
443 iv
= convert_port_to_voucher(port
);
452 ipc_voucher_reference(ipc_voucher_t voucher
)
456 if (IPC_VOUCHER_NULL
== voucher
)
459 refs
= iv_reference(voucher
);
464 ipc_voucher_release(ipc_voucher_t voucher
)
466 if (IPC_VOUCHER_NULL
!= voucher
)
471 * Routine: ipc_voucher_notify
473 * Called whenever the Mach port system detects no-senders
474 * on the voucher port.
476 * Each time the send-right count goes positive, a no-senders
477 * notification is armed (and a voucher reference is donated).
478 * So, each notification that comes in must release a voucher
479 * reference. If more send rights have been added since it
480 * fired (asynchronously), they will be protected by a different
484 ipc_voucher_notify(mach_msg_header_t
*msg
)
486 mach_no_senders_notification_t
*notification
= (void *)msg
;
487 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
490 assert(ip_active(port
));
491 assert(IKOT_VOUCHER
== ip_kotype(port
));
492 iv
= (ipc_voucher_t
)port
->ip_kobject
;
494 ipc_voucher_release(iv
);
498 * Convert a voucher to a port.
501 convert_voucher_to_port(ipc_voucher_t voucher
)
503 ipc_port_t port
, send
;
505 if (IV_NULL
== voucher
)
508 assert(0 < voucher
->iv_refs
);
510 /* create a port if needed */
511 port
= voucher
->iv_port
;
512 if (!IP_VALID(port
)) {
513 port
= ipc_port_alloc_kernel();
514 assert(IP_VALID(port
));
515 ipc_kobject_set_atomically(port
, (ipc_kobject_t
) voucher
, IKOT_VOUCHER
);
517 /* If we lose the race, deallocate and pick up the other guy's port */
518 if (!OSCompareAndSwapPtr(IP_NULL
, port
, &voucher
->iv_port
)) {
519 ipc_port_dealloc_kernel(port
);
520 port
= voucher
->iv_port
;
521 assert(ip_kotype(port
) == IKOT_VOUCHER
);
522 assert(port
->ip_kobject
== (ipc_kobject_t
)voucher
);
527 assert(ip_active(port
));
528 send
= ipc_port_make_send_locked(port
);
530 if (1 == port
->ip_srights
) {
531 ipc_port_t old_notify
;
533 /* transfer our ref to the port, and arm the no-senders notification */
534 assert(IP_NULL
== port
->ip_nsrequest
);
535 ipc_port_nsrequest(port
, port
->ip_mscount
, ipc_port_make_sonce_locked(port
), &old_notify
);
537 assert(IP_NULL
== old_notify
);
539 /* piggyback on the existing port reference, so consume ours */
541 ipc_voucher_release(voucher
);
546 #define ivace_reset_data(ivace_elem, next_index) { \
547 (ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE; \
548 (ivace_elem)->ivace_refs = 0; \
549 (ivace_elem)->ivace_persist = 0; \
550 (ivace_elem)->ivace_made = 0; \
551 (ivace_elem)->ivace_free = TRUE; \
552 (ivace_elem)->ivace_releasing = FALSE; \
553 (ivace_elem)->ivace_layered = 0; \
554 (ivace_elem)->ivace_index = IV_HASH_END; \
555 (ivace_elem)->ivace_next = (next_index); \
558 #define ivace_copy_data(ivace_src_elem, ivace_dst_elem) { \
559 (ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
560 (ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs; \
561 (ivace_dst_elem)->ivace_persist = (ivace_src_elem)->ivace_persist; \
562 (ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made; \
563 (ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free; \
564 (ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered; \
565 (ivace_dst_elem)->ivace_releasing = (ivace_src_elem)->ivace_releasing; \
566 (ivace_dst_elem)->ivace_index = (ivace_src_elem)->ivace_index; \
567 (ivace_dst_elem)->ivace_next = (ivace_src_elem)->ivace_next; \
570 ipc_voucher_attr_control_t
571 ivac_alloc(iv_index_t key_index
)
573 ipc_voucher_attr_control_t ivac
;
578 ivac
= (ipc_voucher_attr_control_t
)zalloc(ipc_voucher_attr_control_zone
);
579 if (IVAC_NULL
== ivac
)
583 ivac
->ivac_is_growing
= FALSE
;
584 ivac
->ivac_port
= IP_NULL
;
586 /* start with just the inline table */
587 table
= (ivac_entry_t
) kalloc(IVAC_ENTRIES_MIN
* sizeof(ivac_entry
));
588 ivac
->ivac_table
= table
;
589 ivac
->ivac_table_size
= IVAC_ENTRIES_MIN
;
590 ivac
->ivac_init_table_size
= IVAC_ENTRIES_MIN
;
591 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
592 ivace_reset_data(&table
[i
], i
+1);
595 /* the default table entry is never on freelist */
596 table
[0].ivace_next
= IV_HASH_END
;
597 table
[0].ivace_free
= FALSE
;
598 table
[i
-1].ivace_next
= IV_FREELIST_END
;
599 ivac
->ivac_freelist
= 1;
600 ivac_lock_init(ivac
);
601 ivac
->ivac_key_index
= key_index
;
607 ivac_dealloc(ipc_voucher_attr_control_t ivac
)
609 ipc_voucher_attr_manager_t ivam
= IVAM_NULL
;
610 iv_index_t key_index
= ivac
->ivac_key_index
;
611 ipc_port_t port
= ivac
->ivac_port
;
615 * If the control is in the global table, we
616 * have to remove it from there before we (re)confirm
617 * that the reference count is still zero.
620 if (ivac
->ivac_refs
> 0) {
625 /* take it out of the global table */
626 if (iv_global_table
[key_index
].ivgte_control
== ivac
) {
627 ivam
= iv_global_table
[key_index
].ivgte_manager
;
628 iv_global_table
[key_index
].ivgte_manager
= IVAM_NULL
;
629 iv_global_table
[key_index
].ivgte_control
= IVAC_NULL
;
630 iv_global_table
[key_index
].ivgte_key
= MACH_VOUCHER_ATTR_KEY_NONE
;
634 /* release the reference held on the resource manager */
635 if (IVAM_NULL
!= ivam
)
636 (ivam
->ivam_release
)(ivam
);
639 * if a port was allocated for this voucher,
640 * it must not have any remaining send rights,
641 * because the port's reference on the voucher
642 * is gone. We can just discard it now.
644 if (IP_VALID(port
)) {
645 assert(ip_active(port
));
646 assert(port
->ip_srights
== 0);
648 ipc_port_dealloc_kernel(port
);
652 * the resource manager's control reference and all references
653 * held by the specific value caches are gone, so free the
657 for (i
= 0; i
< ivac
->ivac_table_size
; i
++)
658 if (ivac
->ivac_table
[i
].ivace_refs
!= 0)
659 panic("deallocing a resource manager with live refs to its attr values\n");
661 kfree(ivac
->ivac_table
, ivac
->ivac_table_size
* sizeof(*ivac
->ivac_table
));
662 ivac_lock_destroy(ivac
);
663 zfree(ipc_voucher_attr_control_zone
, ivac
);
667 ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control
)
669 ivac_reference(control
);
673 ipc_voucher_attr_control_release(ipc_voucher_attr_control_t control
)
675 ivac_release(control
);
679 * Routine: convert_port_to_voucher_attr_control reference
681 * Convert from a port to a voucher attribute control.
682 * Doesn't consume the port ref; produces a voucher ref,
687 ipc_voucher_attr_control_t
688 convert_port_to_voucher_attr_control(
691 if (IP_VALID(port
)) {
692 ipc_voucher_attr_control_t ivac
= (ipc_voucher_attr_control_t
) port
->ip_kobject
;
695 * No need to lock because we have a reference on the
696 * port, and if it is a true voucher control port,
697 * that reference keeps the voucher bound to the port
700 if (ip_kotype(port
) != IKOT_VOUCHER_ATTR_CONTROL
)
703 assert(ip_active(port
));
705 ivac_reference(ivac
);
712 ipc_voucher_attr_control_notify(mach_msg_header_t
*msg
)
714 mach_no_senders_notification_t
*notification
= (void *)msg
;
715 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
716 ipc_voucher_attr_control_t ivac
;
718 assert(IKOT_VOUCHER_ATTR_CONTROL
== ip_kotype(port
));
720 assert(ip_active(port
));
722 /* if no new send rights, drop a control reference */
723 if (port
->ip_mscount
== notification
->not_count
) {
724 ivac
= (ipc_voucher_attr_control_t
)port
->ip_kobject
;
734 * Convert a voucher attr control to a port.
737 convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control
)
739 ipc_port_t port
, send
;
741 if (IVAC_NULL
== control
)
744 /* create a port if needed */
745 port
= control
->ivac_port
;
746 if (!IP_VALID(port
)) {
747 port
= ipc_port_alloc_kernel();
748 assert(IP_VALID(port
));
749 if (OSCompareAndSwapPtr(IP_NULL
, port
, &control
->ivac_port
)) {
751 ipc_kobject_set_atomically(port
, (ipc_kobject_t
) control
, IKOT_VOUCHER_ATTR_CONTROL
);
753 ipc_port_dealloc_kernel(port
);
754 port
= control
->ivac_port
;
756 assert(ip_kotype(port
) == IKOT_VOUCHER_ATTR_CONTROL
);
757 assert(port
->ip_kobject
== (ipc_kobject_t
)control
);
762 assert(ip_active(port
));
763 send
= ipc_port_make_send_locked(port
);
765 if (1 == port
->ip_srights
) {
766 ipc_port_t old_notify
;
768 /* transfer our ref to the port, and arm the no-senders notification */
769 assert(IP_NULL
== port
->ip_nsrequest
);
770 ipc_port_nsrequest(port
, port
->ip_mscount
, ipc_port_make_sonce_locked(port
), &old_notify
);
771 assert(IP_NULL
== old_notify
);
772 /* ipc_port_nsrequest unlocks the port */
774 /* piggyback on the existing port reference, so consume ours */
776 ivac_release(control
);
782 * Look up the values for a given <key, index> pair.
786 iv_index_t key_index
,
787 iv_index_t value_index
,
788 mach_voucher_attr_value_handle_array_t values
,
789 mach_voucher_attr_value_handle_array_size_t
*count
)
791 ipc_voucher_attr_control_t ivac
;
794 if (IV_UNUSED_VALINDEX
== value_index
||
795 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
<= key_index
) {
800 ivac
= iv_global_table
[key_index
].ivgte_control
;
801 assert(IVAC_NULL
!= ivac
);
804 * Get the entry and then the linked values.
807 assert(value_index
< ivac
->ivac_table_size
);
808 ivace
= &ivac
->ivac_table
[value_index
];
811 * TODO: support chained values (for effective vouchers).
813 assert(ivace
->ivace_refs
> 0);
814 values
[0] = ivace
->ivace_value
;
820 * ivac_grow_table - Allocate a bigger table of attribute values
822 * Conditions: ivac is locked on entry and again on return
825 ivac_grow_table(ipc_voucher_attr_control_t ivac
)
829 /* NOTE: do not modify *_table and *_size values once set */
830 ivac_entry_t new_table
= NULL
, old_table
= NULL
;
831 iv_index_t new_size
, old_size
;
833 if (ivac
->ivac_is_growing
) {
838 ivac
->ivac_is_growing
= 1;
839 if (ivac
->ivac_table_size
>= IVAC_ENTRIES_MAX
) {
840 panic("Cannot grow ipc space beyond IVAC_ENTRIES_MAX. Some process is leaking vouchers");
844 old_size
= ivac
->ivac_table_size
;
847 new_size
= old_size
* 2;
849 assert(new_size
> old_size
);
850 assert(new_size
< IVAC_ENTRIES_MAX
);
852 new_table
= kalloc(sizeof(ivac_entry
) * new_size
);
854 panic("Failed to grow ivac table to size %d\n", new_size
);
858 /* setup the free list for new entries */
859 for (i
= old_size
; i
< new_size
; i
++) {
860 ivace_reset_data(&new_table
[i
], i
+1);
865 for (i
= 0; i
< ivac
->ivac_table_size
; i
++){
866 ivace_copy_data(&ivac
->ivac_table
[i
], &new_table
[i
]);
869 old_table
= ivac
->ivac_table
;
871 ivac
->ivac_table
= new_table
;
872 ivac
->ivac_table_size
= new_size
;
874 /* adding new free entries at head of freelist */
875 ivac
->ivac_table
[new_size
- 1].ivace_next
= ivac
->ivac_freelist
;
876 ivac
->ivac_freelist
= old_size
;
877 ivac
->ivac_is_growing
= 0;
882 kfree(old_table
, old_size
* sizeof(ivac_entry
));
888 * ivace_reference_by_index
890 * Take an additional reference on the <key_index, val_index>
891 * cached value. It is assumed the caller already holds a
892 * reference to the same cached key-value pair.
895 ivace_reference_by_index(
896 iv_index_t key_index
,
897 iv_index_t val_index
)
899 ipc_voucher_attr_control_t ivac
;
902 if (IV_UNUSED_VALINDEX
== val_index
)
905 ivgt_lookup(key_index
, FALSE
, NULL
, &ivac
);
906 assert(IVAC_NULL
!= ivac
);
909 assert(val_index
< ivac
->ivac_table_size
);
910 ivace
= &ivac
->ivac_table
[val_index
];
912 assert(0xdeadc0dedeadc0de != ivace
->ivace_value
);
913 assert(0 < ivace
->ivace_refs
);
914 assert(!ivace
->ivace_free
);
916 /* Take ref only on non-persistent values */
917 if (!ivace
->ivace_persist
) {
925 * Look up the values for a given <key, index> pair.
927 * Consumes a reference on the passed voucher control.
928 * Either it is donated to a newly-created value cache
929 * or it is released (if we piggy back on an existing
930 * value cache entry).
933 ivace_reference_by_value(
934 ipc_voucher_attr_control_t ivac
,
935 mach_voucher_attr_value_handle_t value
,
936 mach_voucher_attr_value_flags_t flag
)
938 ivac_entry_t ivace
= IVACE_NULL
;
939 iv_index_t hash_index
;
942 if (IVAC_NULL
== ivac
) {
943 return IV_UNUSED_VALINDEX
;
948 hash_index
= IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
949 index
= ivac
->ivac_table
[hash_index
].ivace_index
;
950 while (index
!= IV_HASH_END
) {
951 assert(index
< ivac
->ivac_table_size
);
952 ivace
= &ivac
->ivac_table
[index
];
953 assert(!ivace
->ivace_free
);
955 if (ivace
->ivace_value
== value
)
958 assert(ivace
->ivace_next
!= index
);
959 index
= ivace
->ivace_next
;
963 if (index
!= IV_HASH_END
) {
964 /* only add reference on non-persistent value */
965 if (!ivace
->ivace_persist
) {
975 /* insert new entry in the table */
976 index
= ivac
->ivac_freelist
;
977 if (IV_FREELIST_END
== index
) {
979 ivac_grow_table(ivac
);
983 /* take the entry off the freelist */
984 ivace
= &ivac
->ivac_table
[index
];
985 ivac
->ivac_freelist
= ivace
->ivace_next
;
987 /* initialize the new entry */
988 ivace
->ivace_value
= value
;
989 ivace
->ivace_refs
= 1;
990 ivace
->ivace_made
= 1;
991 ivace
->ivace_free
= FALSE
;
992 ivace
->ivace_persist
= (flag
& MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
) ? TRUE
: FALSE
;
994 /* insert the new entry in the proper hash chain */
995 ivace
->ivace_next
= ivac
->ivac_table
[hash_index
].ivace_index
;
996 ivac
->ivac_table
[hash_index
].ivace_index
= index
;
999 /* donated passed in ivac reference to new entry */
1005 * Release a reference on the given <key_index, value_index> pair.
1007 * Conditions: called with nothing locked, as it may cause
1008 * callouts and/or messaging to the resource
1011 static void ivace_release(
1012 iv_index_t key_index
,
1013 iv_index_t value_index
)
1015 ipc_voucher_attr_control_t ivac
;
1016 ipc_voucher_attr_manager_t ivam
;
1017 mach_voucher_attr_value_handle_t value
;
1018 mach_voucher_attr_value_reference_t made
;
1019 mach_voucher_attr_key_t key
;
1020 iv_index_t hash_index
;
1024 /* cant release the default value */
1025 if (IV_UNUSED_VALINDEX
== value_index
)
1028 ivgt_lookup(key_index
, FALSE
, &ivam
, &ivac
);
1029 assert(IVAC_NULL
!= ivac
);
1030 assert(IVAM_NULL
!= ivam
);
1033 assert(value_index
< ivac
->ivac_table_size
);
1034 ivace
= &ivac
->ivac_table
[value_index
];
1036 assert(0 < ivace
->ivace_refs
);
1038 /* cant release persistent values */
1039 if (ivace
->ivace_persist
) {
1044 if (0 < --ivace
->ivace_refs
) {
1049 key
= iv_index_to_key(key_index
);
1050 assert(MACH_VOUCHER_ATTR_KEY_NONE
!= key
);
1053 * if last return reply is still pending,
1054 * let it handle this later return when
1055 * the previous reply comes in.
1057 if (ivace
->ivace_releasing
) {
1062 /* claim releasing */
1063 ivace
->ivace_releasing
= TRUE
;
1064 value
= ivace
->ivace_value
;
1067 assert(value
== ivace
->ivace_value
);
1068 assert(!ivace
->ivace_free
);
1069 made
= ivace
->ivace_made
;
1072 /* callout to manager's release_value */
1073 kr
= (ivam
->ivam_release_value
)(ivam
, key
, value
, made
);
1075 /* recalculate entry address as table may have changed */
1077 ivace
= &ivac
->ivac_table
[value_index
];
1078 assert(value
== ivace
->ivace_value
);
1081 * new made values raced with this return. If the
1082 * manager OK'ed the prior release, we have to start
1083 * the made numbering over again (pretend the race
1084 * didn't happen). If the entry has zero refs again,
1085 * re-drive the release.
1087 if (ivace
->ivace_made
!= made
) {
1088 if (KERN_SUCCESS
== kr
)
1089 ivace
->ivace_made
-= made
;
1091 if (0 == ivace
->ivace_refs
)
1094 ivace
->ivace_releasing
= FALSE
;
1099 * If the manager returned FAILURE, someone took a
1100 * reference on the value but have not updated the ivace,
1101 * release the lock and return since thread who got
1102 * the new reference will update the ivace and will have
1103 * non-zero reference on the value.
1105 if (KERN_SUCCESS
!= kr
) {
1106 ivace
->ivace_releasing
= FALSE
;
1112 assert(0 == ivace
->ivace_refs
);
1115 * going away - remove entry from its hash
1116 * If its at the head of the hash bucket list (common), unchain
1117 * at the head. Otherwise walk the chain until the next points
1118 * at this entry, and remove it from the the list there.
1120 hash_index
= iv_hash_value(key_index
, value
);
1121 if (ivac
->ivac_table
[hash_index
].ivace_index
== value_index
) {
1122 ivac
->ivac_table
[hash_index
].ivace_index
= ivace
->ivace_next
;
1124 hash_index
= ivac
->ivac_table
[hash_index
].ivace_index
;
1125 assert(IV_HASH_END
!= hash_index
);
1126 while (ivac
->ivac_table
[hash_index
].ivace_next
!= value_index
) {
1127 hash_index
= ivac
->ivac_table
[hash_index
].ivace_next
;
1128 assert(IV_HASH_END
!= hash_index
);
1130 ivac
->ivac_table
[hash_index
].ivace_next
= ivace
->ivace_next
;
1133 /* Put this entry on the freelist */
1134 ivace
->ivace_value
= 0xdeadc0dedeadc0de;
1135 ivace
->ivace_releasing
= FALSE
;
1136 ivace
->ivace_free
= TRUE
;
1137 ivace
->ivace_made
= 0;
1138 ivace
->ivace_next
= ivac
->ivac_freelist
;
1139 ivac
->ivac_freelist
= value_index
;
1142 /* release the reference this value held on its cache control */
1152 * Lookup an entry in the global table from the context of a manager
1153 * registration. Adds a reference to the control to keep the results
1154 * around (if needed).
1156 * Because of the calling point, we can't be sure the manager is
1157 * [fully] registered yet. So, we must hold the global table lock
1158 * during the lookup to synchronize with in-parallel registrations
1159 * (and possible table growth).
1162 ivgt_lookup(iv_index_t key_index
,
1163 boolean_t take_reference
,
1164 ipc_voucher_attr_manager_t
*manager
,
1165 ipc_voucher_attr_control_t
*control
)
1167 ipc_voucher_attr_control_t ivac
;
1169 if (key_index
< MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
) {
1171 if (NULL
!= manager
)
1172 *manager
= iv_global_table
[key_index
].ivgte_manager
;
1173 ivac
= iv_global_table
[key_index
].ivgte_control
;
1174 if (IVAC_NULL
!= ivac
) {
1175 assert(key_index
== ivac
->ivac_key_index
);
1176 if (take_reference
) {
1177 assert(NULL
!= control
);
1178 ivac_reference(ivac
);
1182 if (NULL
!= control
)
1185 if (NULL
!= manager
)
1186 *manager
= IVAM_NULL
;
1187 if (NULL
!= control
)
1188 *control
= IVAC_NULL
;
1193 * Routine: ipc_replace_voucher_value
1195 * Replace the <voucher, key> value with the results of
1196 * running the supplied command through the resource
1197 * manager's get-value callback.
1199 * Nothing locked (may invoke user-space repeatedly).
1200 * Caller holds references on voucher and previous voucher.
1202 static kern_return_t
1203 ipc_replace_voucher_value(
1204 ipc_voucher_t voucher
,
1205 mach_voucher_attr_key_t key
,
1206 mach_voucher_attr_recipe_command_t command
,
1207 ipc_voucher_t prev_voucher
,
1208 mach_voucher_attr_content_t content
,
1209 mach_voucher_attr_content_size_t content_size
)
1211 mach_voucher_attr_value_handle_t previous_vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1212 mach_voucher_attr_value_handle_array_size_t previous_vals_count
;
1213 mach_voucher_attr_value_handle_t new_value
;
1214 mach_voucher_attr_value_flags_t new_flag
;
1215 ipc_voucher_t new_value_voucher
;
1216 ipc_voucher_attr_manager_t ivam
;
1217 ipc_voucher_attr_control_t ivac
;
1218 iv_index_t prev_val_index
;
1219 iv_index_t save_val_index
;
1220 iv_index_t val_index
;
1221 iv_index_t key_index
;
1225 * Get the manager for this key_index.
1226 * Returns a reference on the control.
1228 key_index
= iv_key_to_index(key
);
1229 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1230 if (IVAM_NULL
== ivam
)
1231 return KERN_INVALID_ARGUMENT
;
1233 /* save the current value stored in the forming voucher */
1234 save_val_index
= iv_lookup(voucher
, key_index
);
1237 * Get the previous value(s) for this key creation.
1238 * If a previous voucher is specified, they come from there.
1239 * Otherwise, they come from the intermediate values already
1240 * in the forming voucher.
1242 prev_val_index
= (IV_NULL
!= prev_voucher
) ?
1243 iv_lookup(prev_voucher
, key_index
) :
1245 ivace_lookup_values(key_index
, prev_val_index
,
1246 previous_vals
, &previous_vals_count
);
1248 /* Call out to resource manager to get new value */
1249 new_value_voucher
= IV_NULL
;
1250 kr
= (ivam
->ivam_get_value
)(
1252 previous_vals
, previous_vals_count
,
1253 content
, content_size
,
1254 &new_value
, &new_flag
, &new_value_voucher
);
1255 if (KERN_SUCCESS
!= kr
) {
1260 /* TODO: value insertion from returned voucher */
1261 if (IV_NULL
!= new_value_voucher
)
1262 iv_release(new_value_voucher
);
1265 * Find or create a slot in the table associated
1266 * with this attribute value. The ivac reference
1267 * is transferred to a new value, or consumed if
1268 * we find a matching existing value.
1270 val_index
= ivace_reference_by_value(ivac
, new_value
, new_flag
);
1271 iv_set(voucher
, key_index
, val_index
);
1274 * release saved old value from the newly forming voucher
1275 * This is saved until the end to avoid churning the
1276 * release logic in cases where the same value is returned
1277 * as was there before.
1279 ivace_release(key_index
, save_val_index
);
1281 return KERN_SUCCESS
;
1285 * Routine: ipc_directly_replace_voucher_value
1287 * Replace the <voucher, key> value with the value-handle
1288 * supplied directly by the attribute manager.
1291 * Caller holds references on voucher.
1292 * A made reference to the value-handle is donated by the caller.
1294 static kern_return_t
1295 ipc_directly_replace_voucher_value(
1296 ipc_voucher_t voucher
,
1297 mach_voucher_attr_key_t key
,
1298 mach_voucher_attr_value_handle_t new_value
)
1300 ipc_voucher_attr_manager_t ivam
;
1301 ipc_voucher_attr_control_t ivac
;
1302 iv_index_t save_val_index
;
1303 iv_index_t val_index
;
1304 iv_index_t key_index
;
1307 * Get the manager for this key_index.
1308 * Returns a reference on the control.
1310 key_index
= iv_key_to_index(key
);
1311 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1312 if (IVAM_NULL
== ivam
)
1313 return KERN_INVALID_ARGUMENT
;
1315 /* save the current value stored in the forming voucher */
1316 save_val_index
= iv_lookup(voucher
, key_index
);
1319 * Find or create a slot in the table associated
1320 * with this attribute value. The ivac reference
1321 * is transferred to a new value, or consumed if
1322 * we find a matching existing value.
1324 val_index
= ivace_reference_by_value(ivac
, new_value
,
1325 MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
);
1326 iv_set(voucher
, key_index
, val_index
);
1329 * release saved old value from the newly forming voucher
1330 * This is saved until the end to avoid churning the
1331 * release logic in cases where the same value is returned
1332 * as was there before.
1334 ivace_release(key_index
, save_val_index
);
1336 return KERN_SUCCESS
;
1339 static kern_return_t
1340 ipc_execute_voucher_recipe_command(
1341 ipc_voucher_t voucher
,
1342 mach_voucher_attr_key_t key
,
1343 mach_voucher_attr_recipe_command_t command
,
1344 ipc_voucher_t prev_iv
,
1345 mach_voucher_attr_content_t content
,
1346 mach_voucher_attr_content_size_t content_size
,
1349 iv_index_t prev_val_index
;
1350 iv_index_t val_index
;
1356 * MACH_VOUCHER_ATTR_COPY
1357 * Copy the attribute(s) from the previous voucher to the new
1358 * one. A wildcard key is an acceptable value - indicating a
1359 * desire to copy all the attribute values from the previous
1362 case MACH_VOUCHER_ATTR_COPY
:
1364 /* no recipe data on a copy */
1365 if (0 < content_size
)
1366 return KERN_INVALID_ARGUMENT
;
1368 /* nothing to copy from? - done */
1369 if (IV_NULL
== prev_iv
)
1370 return KERN_SUCCESS
;
1372 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1373 iv_index_t limit
, j
;
1375 /* reconcile possible difference in voucher sizes */
1376 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1377 prev_iv
->iv_table_size
:
1378 voucher
->iv_table_size
;
1380 /* wildcard matching */
1381 for (j
= 0; j
< limit
; j
++) {
1382 /* release old value being replaced */
1383 val_index
= iv_lookup(voucher
, j
);
1384 ivace_release(j
, val_index
);
1386 /* replace with reference to prev voucher's value */
1387 prev_val_index
= iv_lookup(prev_iv
, j
);
1388 ivace_reference_by_index(j
, prev_val_index
);
1389 iv_set(voucher
, j
, prev_val_index
);
1392 iv_index_t key_index
;
1394 /* copy just one key */
1395 key_index
= iv_key_to_index(key
);
1396 if (ivgt_keys_in_use
< key_index
)
1397 return KERN_INVALID_ARGUMENT
;
1399 /* release old value being replaced */
1400 val_index
= iv_lookup(voucher
, key_index
);
1401 ivace_release(key_index
, val_index
);
1403 /* replace with reference to prev voucher's value */
1404 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1405 ivace_reference_by_index(key_index
, prev_val_index
);
1406 iv_set(voucher
, key_index
, prev_val_index
);
1411 * MACH_VOUCHER_ATTR_REMOVE
1412 * Remove the attribute(s) from the under construction voucher.
1413 * A wildcard key is an acceptable value - indicating a desire
1414 * to remove all the attribute values set up so far in the voucher.
1415 * If a previous voucher is specified, only remove the value it
1416 * it matches the value in the previous voucher.
1418 case MACH_VOUCHER_ATTR_REMOVE
:
1419 /* no recipe data on a remove */
1420 if (0 < content_size
)
1421 return KERN_INVALID_ARGUMENT
;
1423 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1424 iv_index_t limit
, j
;
1426 /* reconcile possible difference in voucher sizes */
1427 limit
= (IV_NULL
== prev_iv
) ? voucher
->iv_table_size
:
1428 ((prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1429 prev_iv
->iv_table_size
: voucher
->iv_table_size
);
1431 /* wildcard matching */
1432 for (j
= 0; j
< limit
; j
++) {
1433 val_index
= iv_lookup(voucher
, j
);
1435 /* If not matched in previous, skip */
1436 if (IV_NULL
!= prev_iv
) {
1437 prev_val_index
= iv_lookup(prev_iv
, j
);
1438 if (val_index
!= prev_val_index
)
1441 /* release and clear */
1442 ivace_release(j
, val_index
);
1443 iv_set(voucher
, j
, IV_UNUSED_VALINDEX
);
1446 iv_index_t key_index
;
1448 /* copy just one key */
1449 key_index
= iv_key_to_index(key
);
1450 if (ivgt_keys_in_use
< key_index
)
1451 return KERN_INVALID_ARGUMENT
;
1453 val_index
= iv_lookup(voucher
, key_index
);
1455 /* If not matched in previous, skip */
1456 if (IV_NULL
!= prev_iv
) {
1457 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1458 if (val_index
!= prev_val_index
)
1462 /* release and clear */
1463 ivace_release(key_index
, val_index
);
1464 iv_set(voucher
, key_index
, IV_UNUSED_VALINDEX
);
1469 * MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
1470 * Use key-privilege to set a value handle for the attribute directly,
1471 * rather than triggering a callback into the attribute manager to
1472 * interpret a recipe to generate the value handle.
1474 case MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
:
1476 mach_voucher_attr_value_handle_t new_value
;
1478 if (sizeof(mach_voucher_attr_value_handle_t
) != content_size
)
1479 return KERN_INVALID_ARGUMENT
;
1481 new_value
= *(mach_voucher_attr_value_handle_t
*)(void *)content
;
1482 kr
= ipc_directly_replace_voucher_value(voucher
,
1485 if (KERN_SUCCESS
!= kr
)
1488 return KERN_INVALID_CAPABILITY
;
1492 * MACH_VOUCHER_ATTR_REDEEM
1493 * Redeem the attribute(s) from the previous voucher for a possibly
1494 * new value in the new voucher. A wildcard key is an acceptable value,
1495 * indicating a desire to redeem all the values.
1497 case MACH_VOUCHER_ATTR_REDEEM
:
1499 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1500 iv_index_t limit
, j
;
1502 /* reconcile possible difference in voucher sizes */
1503 if (IV_NULL
!= prev_iv
)
1504 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1505 prev_iv
->iv_table_size
:
1506 voucher
->iv_table_size
;
1508 limit
= voucher
->iv_table_size
;
1510 /* wildcard matching */
1511 for (j
= 0; j
< limit
; j
++) {
1512 mach_voucher_attr_key_t j_key
;
1514 j_key
= iv_index_to_key(j
);
1516 /* skip non-existent managers */
1517 if (MACH_VOUCHER_ATTR_KEY_NONE
== j_key
)
1520 /* get the new value from redeem (skip empty previous) */
1521 kr
= ipc_replace_voucher_value(voucher
,
1527 if (KERN_SUCCESS
!= kr
)
1532 /* fall thru for single key redemption */
1536 * Replace the current value for the <voucher, key> pair with whatever
1537 * value the resource manager returns for the command and recipe
1538 * combination provided.
1541 kr
= ipc_replace_voucher_value(voucher
,
1547 if (KERN_SUCCESS
!= kr
)
1552 return KERN_SUCCESS
;
1556 * Routine: iv_checksum
1558 * Compute the voucher sum. This is more position-
1559 * relevant than many other checksums - important for
1560 * vouchers (arrays of low, oft-reused, indexes).
1562 static inline iv_index_t
1563 iv_checksum(ipc_voucher_t voucher
, boolean_t
*emptyp
)
1567 boolean_t empty
= TRUE
;
1568 if (0 < voucher
->iv_table_size
) {
1569 iv_index_t i
= voucher
->iv_table_size
- 1;
1572 iv_index_t v
= voucher
->iv_table
[i
];
1573 c
= c
<< 3 | c
>> (32 - 3); /* rotate */
1574 c
= ~c
; /* invert */
1576 c
+= v
; /* add in */
1588 * See if the set of values represented by this new voucher
1589 * already exist in another voucher. If so return a reference
1590 * to the existing voucher and deallocate the voucher provided.
1591 * Otherwise, insert this one in the hash and return it.
1593 * A voucher reference is donated on entry.
1595 * A voucher reference (may be different than on entry).
1597 static ipc_voucher_t
1598 iv_dedup(ipc_voucher_t new_iv
)
1605 sum
= iv_checksum(new_iv
, &empty
);
1607 /* If all values are default, that's the empty (NULL) voucher */
1609 iv_dealloc(new_iv
, FALSE
);
1613 hash
= IV_HASH_BUCKET(sum
);
1616 queue_iterate(&ivht_bucket
[hash
], iv
, ipc_voucher_t
, iv_hash_link
) {
1617 assert(iv
->iv_hash
== hash
);
1619 /* if not already deallocating and sums match... */
1620 if (0 < iv
->iv_refs
&& iv
->iv_sum
== sum
) {
1624 assert(iv
->iv_table_size
<= new_iv
->iv_table_size
);
1626 /* and common entries match... */
1627 for (i
= 0; i
< iv
->iv_table_size
; i
++)
1628 if (iv
->iv_table
[i
] != new_iv
->iv_table
[i
])
1630 if (i
< iv
->iv_table_size
)
1633 /* and all extra entries in new one are unused... */
1634 while (i
< new_iv
->iv_table_size
)
1635 if (new_iv
->iv_table
[i
++] != IV_UNUSED_VALINDEX
)
1637 if (i
< new_iv
->iv_table_size
)
1640 /* ... we found a match... */
1642 /* can we get a ref before it hits 0
1644 * This is thread safe. The reference is just an atomic
1645 * add. If the reference count is zero when we adjust it,
1646 * no other thread can have a reference to the voucher.
1647 * The dealloc code requires holding the ivht_lock, so
1648 * the voucher cannot be yanked out from under us.
1650 refs
= iv_reference(iv
);
1652 /* drats! going away. Put back to zero */
1659 /* referenced previous, so deallocate the new one */
1660 iv_dealloc(new_iv
, FALSE
);
1665 /* add the new voucher to the hash, and return it */
1666 new_iv
->iv_sum
= sum
;
1667 new_iv
->iv_hash
= hash
;
1668 queue_enter(&ivht_bucket
[hash
], new_iv
, ipc_voucher_t
, iv_hash_link
);
1673 * This code is disabled for KDEBUG_LEVEL_IST and KDEBUG_LEVEL_NONE
1675 #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD)
1676 if (kdebug_enable
& ~KDEBUG_ENABLE_PPT
) {
1677 uintptr_t voucher_addr
= VM_KERNEL_ADDRPERM((uintptr_t)new_iv
);
1678 uintptr_t attr_tracepoints_needed
= 0;
1680 if (ipc_voucher_trace_contents
) {
1682 * voucher_contents sizing is a bit more constrained
1683 * than might be obvious.
1685 * This is typically a uint8_t typed array. However,
1686 * we want to access it as a uintptr_t to efficiently
1687 * copyout the data in tracepoints.
1689 * This constrains the size to uintptr_t bytes, and
1690 * adds a minimimum alignment requirement equivalent
1693 * Further constraining the size is the fact that it
1694 * is copied out 4 uintptr_t chunks at a time. We do
1695 * NOT want to run off the end of the array and copyout
1696 * random stack data.
1698 * So the minimum size is 4 * sizeof(uintptr_t), and
1699 * the minimum alignment is uintptr_t aligned.
1702 #define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
1703 #define PAYLOAD_SIZE 1024
1705 static_assert(PAYLOAD_SIZE
% PAYLOAD_PER_TRACEPOINT
== 0, "size invariant violated");
1707 mach_voucher_attr_raw_recipe_array_size_t payload_size
= PAYLOAD_SIZE
;
1708 uintptr_t payload
[PAYLOAD_SIZE
/ sizeof(uintptr_t)];
1711 kr
= mach_voucher_extract_all_attr_recipes(new_iv
, (mach_voucher_attr_raw_recipe_array_t
)payload
, &payload_size
);
1712 if (KERN_SUCCESS
== kr
) {
1713 attr_tracepoints_needed
= (payload_size
+ PAYLOAD_PER_TRACEPOINT
- 1) / PAYLOAD_PER_TRACEPOINT
;
1716 * To prevent leaking data from the stack, we
1717 * need to zero data to the end of a tracepoint
1720 size_t remainder
= payload_size
% PAYLOAD_PER_TRACEPOINT
;
1722 bzero((uint8_t*)payload
+ payload_size
,
1723 PAYLOAD_PER_TRACEPOINT
- remainder
);
1727 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_CREATE
) | DBG_FUNC_NONE
,
1729 new_iv
->iv_table_size
, ivht_count
, payload_size
, 0);
1731 uintptr_t index
= 0;
1732 while (attr_tracepoints_needed
--) {
1733 KERNEL_DEBUG_CONSTANT1(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_CREATE_ATTR_DATA
) | DBG_FUNC_NONE
,
1742 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_CREATE
) | DBG_FUNC_NONE
,
1744 new_iv
->iv_table_size
, ivht_count
, 0, 0);
1747 #endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
1753 * Routine: ipc_create_mach_voucher
1755 * Create a new mach voucher and initialize it with the
1756 * value(s) created by having the appropriate resource
1757 * managers interpret the supplied recipe commands and
1760 * Nothing locked (may invoke user-space repeatedly).
1761 * Caller holds references on previous vouchers.
1762 * Previous vouchers are passed as voucher indexes.
1765 ipc_create_mach_voucher(
1766 ipc_voucher_attr_raw_recipe_array_t recipes
,
1767 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1768 ipc_voucher_t
*new_voucher
)
1770 ipc_voucher_attr_recipe_t sub_recipe
;
1771 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1772 ipc_voucher_t voucher
;
1773 kern_return_t kr
= KERN_SUCCESS
;
1775 /* if nothing to do ... */
1776 if (0 == recipe_size
) {
1777 *new_voucher
= IV_NULL
;
1778 return KERN_SUCCESS
;
1781 /* allocate a voucher */
1782 voucher
= iv_alloc(ivgt_keys_in_use
);
1783 if (IV_NULL
== voucher
)
1784 return KERN_RESOURCE_SHORTAGE
;
1786 /* iterate over the recipe items */
1787 while (0 < recipe_size
- recipe_used
) {
1789 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1790 kr
= KERN_INVALID_ARGUMENT
;
1794 /* find the next recipe */
1795 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1796 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1797 kr
= KERN_INVALID_ARGUMENT
;
1800 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1802 kr
= ipc_execute_voucher_recipe_command(voucher
,
1804 sub_recipe
->command
,
1805 sub_recipe
->previous_voucher
,
1806 sub_recipe
->content
,
1807 sub_recipe
->content_size
,
1809 if (KERN_SUCCESS
!= kr
)
1813 if (KERN_SUCCESS
== kr
) {
1814 *new_voucher
= iv_dedup(voucher
);
1816 iv_dealloc(voucher
, FALSE
);
1817 *new_voucher
= IV_NULL
;
1823 * Routine: ipc_voucher_attr_control_create_mach_voucher
1825 * Create a new mach voucher and initialize it with the
1826 * value(s) created by having the appropriate resource
1827 * managers interpret the supplied recipe commands and
1830 * The resource manager control's privilege over its
1831 * particular key value is reflected on to the execution
1832 * code, allowing internal commands (like setting a
1833 * key value handle directly, rather than having to
1834 * create a recipe, that will generate a callback just
1838 * Nothing locked (may invoke user-space repeatedly).
1839 * Caller holds references on previous vouchers.
1840 * Previous vouchers are passed as voucher indexes.
1843 ipc_voucher_attr_control_create_mach_voucher(
1844 ipc_voucher_attr_control_t control
,
1845 ipc_voucher_attr_raw_recipe_array_t recipes
,
1846 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1847 ipc_voucher_t
*new_voucher
)
1849 mach_voucher_attr_key_t control_key
;
1850 ipc_voucher_attr_recipe_t sub_recipe
;
1851 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1852 ipc_voucher_t voucher
= IV_NULL
;
1853 kern_return_t kr
= KERN_SUCCESS
;
1855 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
)
1856 return KERN_INVALID_CAPABILITY
;
1858 /* if nothing to do ... */
1859 if (0 == recipe_size
) {
1860 *new_voucher
= IV_NULL
;
1861 return KERN_SUCCESS
;
1864 /* allocate new voucher */
1865 voucher
= iv_alloc(ivgt_keys_in_use
);
1866 if (IV_NULL
== voucher
)
1867 return KERN_RESOURCE_SHORTAGE
;
1869 control_key
= iv_index_to_key(control
->ivac_key_index
);
1871 /* iterate over the recipe items */
1872 while (0 < recipe_size
- recipe_used
) {
1874 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1875 kr
= KERN_INVALID_ARGUMENT
;
1879 /* find the next recipe */
1880 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1881 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1882 kr
= KERN_INVALID_ARGUMENT
;
1885 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1887 kr
= ipc_execute_voucher_recipe_command(voucher
,
1889 sub_recipe
->command
,
1890 sub_recipe
->previous_voucher
,
1891 sub_recipe
->content
,
1892 sub_recipe
->content_size
,
1893 (sub_recipe
->key
== control_key
));
1894 if (KERN_SUCCESS
!= kr
)
1898 if (KERN_SUCCESS
== kr
) {
1899 *new_voucher
= iv_dedup(voucher
);
1901 *new_voucher
= IV_NULL
;
1902 iv_dealloc(voucher
, FALSE
);
1908 * ipc_register_well_known_mach_voucher_attr_manager
1910 * Register the resource manager responsible for a given key value.
1913 ipc_register_well_known_mach_voucher_attr_manager(
1914 ipc_voucher_attr_manager_t manager
,
1915 mach_voucher_attr_value_handle_t default_value
,
1916 mach_voucher_attr_key_t key
,
1917 ipc_voucher_attr_control_t
*control
)
1919 ipc_voucher_attr_control_t new_control
;
1920 iv_index_t key_index
;
1921 iv_index_t hash_index
;
1923 if (IVAM_NULL
== manager
)
1924 return KERN_INVALID_ARGUMENT
;
1926 key_index
= iv_key_to_index(key
);
1927 if (IV_UNUSED_KEYINDEX
== key_index
)
1928 return KERN_INVALID_ARGUMENT
;
1930 new_control
= ivac_alloc(key_index
);
1931 if (IVAC_NULL
== new_control
)
1932 return KERN_RESOURCE_SHORTAGE
;
1934 /* insert the default value into slot 0 */
1935 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_value
= default_value
;
1936 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_refs
= IVACE_REFS_MAX
;
1937 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_made
= IVACE_REFS_MAX
;
1938 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_persist
= TRUE
;
1939 assert(IV_HASH_END
== new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_next
);
1942 if (IVAM_NULL
!= iv_global_table
[key_index
].ivgte_manager
) {
1944 ivac_release(new_control
);
1945 return KERN_INVALID_ARGUMENT
;
1948 /* fill in the global table slot for this key */
1949 iv_global_table
[key_index
].ivgte_manager
= manager
;
1950 iv_global_table
[key_index
].ivgte_control
= new_control
;
1951 iv_global_table
[key_index
].ivgte_key
= key
;
1953 /* insert the default value into the hash (in case it is returned later) */
1954 hash_index
= iv_hash_value(key_index
, default_value
);
1955 assert(IV_HASH_END
== new_control
->ivac_table
[hash_index
].ivace_index
);
1956 new_control
->ivac_table
[hash_index
].ivace_index
= IV_UNUSED_VALINDEX
;
1960 /* return the reference on the new cache control to the caller */
1961 *control
= new_control
;
1963 return KERN_SUCCESS
;
1967 * Routine: mach_voucher_extract_attr_content
1969 * Extract the content for a given <voucher, key> pair.
1971 * If a value other than the default is present for this
1972 * <voucher,key> pair, we need to contact the resource
1973 * manager to extract the content/meaning of the value(s)
1974 * present. Otherwise, return success (but no data).
1977 * Nothing locked - as it may upcall to user-space.
1978 * The caller holds a reference on the voucher.
1981 mach_voucher_extract_attr_content(
1982 ipc_voucher_t voucher
,
1983 mach_voucher_attr_key_t key
,
1984 mach_voucher_attr_content_t content
,
1985 mach_voucher_attr_content_size_t
*in_out_size
)
1987 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1988 mach_voucher_attr_value_handle_array_size_t vals_count
;
1989 mach_voucher_attr_recipe_command_t command
;
1990 ipc_voucher_attr_manager_t manager
;
1991 iv_index_t value_index
;
1992 iv_index_t key_index
;
1996 if (IV_NULL
== voucher
)
1997 return KERN_INVALID_ARGUMENT
;
1999 key_index
= iv_key_to_index(key
);
2001 value_index
= iv_lookup(voucher
, key_index
);
2002 if (IV_UNUSED_VALINDEX
== value_index
) {
2004 return KERN_SUCCESS
;
2008 * Get the manager for this key_index. The
2009 * existence of a non-default value for this
2010 * slot within our voucher will keep the
2011 * manager referenced during the callout.
2013 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2014 if (IVAM_NULL
== manager
) {
2015 return KERN_INVALID_ARGUMENT
;
2019 * Get the value(s) to pass to the manager
2020 * for this value_index.
2022 ivace_lookup_values(key_index
, value_index
,
2024 assert(0 < vals_count
);
2026 /* callout to manager */
2028 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2031 content
, in_out_size
);
2036 * Routine: mach_voucher_extract_attr_recipe
2038 * Extract a recipe for a given <voucher, key> pair.
2040 * If a value other than the default is present for this
2041 * <voucher,key> pair, we need to contact the resource
2042 * manager to extract the content/meaning of the value(s)
2043 * present. Otherwise, return success (but no data).
2046 * Nothing locked - as it may upcall to user-space.
2047 * The caller holds a reference on the voucher.
2050 mach_voucher_extract_attr_recipe(
2051 ipc_voucher_t voucher
,
2052 mach_voucher_attr_key_t key
,
2053 mach_voucher_attr_raw_recipe_t raw_recipe
,
2054 mach_voucher_attr_raw_recipe_size_t
*in_out_size
)
2056 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2057 mach_voucher_attr_value_handle_array_size_t vals_count
;
2058 ipc_voucher_attr_manager_t manager
;
2059 mach_voucher_attr_recipe_t recipe
;
2060 iv_index_t value_index
;
2061 iv_index_t key_index
;
2065 if (IV_NULL
== voucher
)
2066 return KERN_INVALID_ARGUMENT
;
2068 key_index
= iv_key_to_index(key
);
2070 value_index
= iv_lookup(voucher
, key_index
);
2071 if (IV_UNUSED_VALINDEX
== value_index
) {
2073 return KERN_SUCCESS
;
2076 if (*in_out_size
< sizeof(*recipe
))
2077 return KERN_NO_SPACE
;
2079 recipe
= (mach_voucher_attr_recipe_t
)(void *)raw_recipe
;
2081 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2082 recipe
->previous_voucher
= MACH_VOUCHER_NAME_NULL
;
2083 recipe
->content_size
= *in_out_size
- sizeof(*recipe
);
2086 * Get the manager for this key_index. The
2087 * existence of a non-default value for this
2088 * slot within our voucher will keep the
2089 * manager referenced during the callout.
2091 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2092 if (IVAM_NULL
== manager
) {
2093 return KERN_INVALID_ARGUMENT
;
2097 * Get the value(s) to pass to the manager
2098 * for this value_index.
2100 ivace_lookup_values(key_index
, value_index
,
2102 assert(0 < vals_count
);
2104 /* callout to manager */
2105 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2108 recipe
->content
, &recipe
->content_size
);
2109 if (KERN_SUCCESS
== kr
) {
2110 assert(*in_out_size
- sizeof(*recipe
) >= recipe
->content_size
);
2111 *in_out_size
= sizeof(*recipe
) + recipe
->content_size
;
2120 * Routine: mach_voucher_extract_all_attr_recipes
2122 * Extract all the (non-default) contents for a given voucher,
2123 * building up a recipe that could be provided to a future
2124 * voucher creation call.
2126 * Nothing locked (may invoke user-space).
2127 * Caller holds a reference on the supplied voucher.
2130 mach_voucher_extract_all_attr_recipes(
2131 ipc_voucher_t voucher
,
2132 mach_voucher_attr_raw_recipe_array_t recipes
,
2133 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2135 mach_voucher_attr_recipe_size_t recipe_size
= *in_out_size
;
2136 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2137 iv_index_t key_index
;
2139 if (IV_NULL
== voucher
)
2140 return KERN_INVALID_ARGUMENT
;
2142 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2143 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2144 mach_voucher_attr_value_handle_array_size_t vals_count
;
2145 mach_voucher_attr_content_size_t content_size
;
2146 ipc_voucher_attr_manager_t manager
;
2147 mach_voucher_attr_recipe_t recipe
;
2148 mach_voucher_attr_key_t key
;
2149 iv_index_t value_index
;
2152 /* don't output anything for a default value */
2153 value_index
= iv_lookup(voucher
, key_index
);
2154 if (IV_UNUSED_VALINDEX
== value_index
)
2157 if (recipe_size
- recipe_used
< sizeof(*recipe
))
2158 return KERN_NO_SPACE
;
2161 * Get the manager for this key_index. The
2162 * existence of a non-default value for this
2163 * slot within our voucher will keep the
2164 * manager referenced during the callout.
2166 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2167 assert(IVAM_NULL
!= manager
);
2168 if (IVAM_NULL
== manager
) {
2172 recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2173 content_size
= recipe_size
- recipe_used
- sizeof(*recipe
);
2176 * Get the value(s) to pass to the manager
2177 * for this value_index.
2179 ivace_lookup_values(key_index
, value_index
,
2181 assert(0 < vals_count
);
2183 key
= iv_index_to_key(key_index
);
2186 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2187 recipe
->content_size
= content_size
;
2189 /* callout to manager */
2190 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2193 recipe
->content
, &recipe
->content_size
);
2194 if (KERN_SUCCESS
!= kr
)
2197 assert(recipe
->content_size
<= content_size
);
2198 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2201 *in_out_size
= recipe_used
;
2202 return KERN_SUCCESS
;
2206 * Routine: mach_voucher_debug_info
2208 * Extract all the (non-default) contents for a given mach port name,
2209 * building up a recipe that could be provided to a future
2210 * voucher creation call.
2212 * Nothing locked (may invoke user-space).
2213 * Caller may not hold a reference on the supplied voucher.
2215 #if !(DEVELOPMENT || DEBUG)
2217 mach_voucher_debug_info(
2218 ipc_space_t __unused space
,
2219 mach_port_name_t __unused voucher_name
,
2220 mach_voucher_attr_raw_recipe_array_t __unused recipes
,
2221 mach_voucher_attr_raw_recipe_array_size_t __unused
*in_out_size
)
2223 return KERN_NOT_SUPPORTED
;
2227 mach_voucher_debug_info(
2229 mach_port_name_t voucher_name
,
2230 mach_voucher_attr_raw_recipe_array_t recipes
,
2231 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2233 ipc_voucher_t voucher
= IPC_VOUCHER_NULL
;
2235 ipc_port_t port
= MACH_PORT_NULL
;
2237 if (!MACH_PORT_VALID(voucher_name
)) {
2238 return KERN_INVALID_ARGUMENT
;
2241 kr
= ipc_port_translate_send(space
, voucher_name
, &port
);
2242 if (KERN_SUCCESS
!= kr
)
2243 return KERN_INVALID_ARGUMENT
;
2245 voucher
= convert_port_to_voucher(port
);
2249 kr
= mach_voucher_extract_all_attr_recipes(voucher
, recipes
, in_out_size
);
2250 ipc_voucher_release(voucher
);
2254 return KERN_FAILURE
;
2259 * Routine: mach_voucher_attr_command
2261 * Invoke an attribute-specific command through this voucher.
2263 * The voucher layout, membership, etc... is not altered
2264 * through the execution of this command.
2267 * Nothing locked - as it may upcall to user-space.
2268 * The caller holds a reference on the voucher.
2271 mach_voucher_attr_command(
2272 ipc_voucher_t voucher
,
2273 mach_voucher_attr_key_t key
,
2274 mach_voucher_attr_command_t command
,
2275 mach_voucher_attr_content_t in_content
,
2276 mach_voucher_attr_content_size_t in_content_size
,
2277 mach_voucher_attr_content_t out_content
,
2278 mach_voucher_attr_content_size_t
*out_content_size
)
2280 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2281 mach_voucher_attr_value_handle_array_size_t vals_count
;
2282 ipc_voucher_attr_manager_t manager
;
2283 ipc_voucher_attr_control_t control
;
2284 iv_index_t value_index
;
2285 iv_index_t key_index
;
2289 if (IV_NULL
== voucher
)
2290 return KERN_INVALID_ARGUMENT
;
2292 key_index
= iv_key_to_index(key
);
2295 * Get the manager for this key_index.
2296 * Allowing commands against the default value
2297 * for an attribute means that we have to hold
2298 * reference on the attribute manager control
2299 * to keep the manager around during the command
2302 ivgt_lookup(key_index
, TRUE
, &manager
, &control
);
2303 if (IVAM_NULL
== manager
) {
2304 return KERN_INVALID_ARGUMENT
;
2308 * Get the values for this <voucher, key> pair
2309 * to pass to the attribute manager. It is still
2310 * permissible to execute a command against the
2311 * default value (empty value array).
2313 value_index
= iv_lookup(voucher
, key_index
);
2314 ivace_lookup_values(key_index
, value_index
,
2317 /* callout to manager */
2318 kr
= (manager
->ivam_command
)(manager
, key
,
2321 in_content
, in_content_size
,
2322 out_content
, out_content_size
);
2324 /* release reference on control */
2325 ivac_release(control
);
2331 * Routine: mach_voucher_attr_control_get_values
2333 * For a given voucher, get the value handle associated with the
2334 * specified attribute manager.
2337 mach_voucher_attr_control_get_values(
2338 ipc_voucher_attr_control_t control
,
2339 ipc_voucher_t voucher
,
2340 mach_voucher_attr_value_handle_array_t out_values
,
2341 mach_voucher_attr_value_handle_array_size_t
*in_out_size
)
2343 iv_index_t key_index
, value_index
;
2345 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
)
2346 return KERN_INVALID_CAPABILITY
;
2348 if (IV_NULL
== voucher
)
2349 return KERN_INVALID_ARGUMENT
;
2351 if (0 == *in_out_size
)
2352 return KERN_SUCCESS
;
2354 key_index
= control
->ivac_key_index
;
2356 assert(0 < voucher
->iv_refs
);
2357 value_index
= iv_lookup(voucher
, key_index
);
2358 ivace_lookup_values(key_index
, value_index
,
2359 out_values
, in_out_size
);
2360 return KERN_SUCCESS
;
2364 * Routine: mach_voucher_attr_control_create_mach_voucher
2366 * Create a new mach voucher and initialize it by processing the
2367 * supplied recipe(s).
2369 * Coming in on the attribute control port denotes special privileges
2370 * over they key associated with the control port.
2372 * Coming in from user-space, each recipe item will have a previous
2373 * recipe port name that needs to be converted to a voucher. Because
2374 * we can't rely on the port namespace to hold a reference on each
2375 * previous voucher port for the duration of processing that command,
2376 * we have to convert the name to a voucher reference and release it
2377 * after the command processing is done.
2380 mach_voucher_attr_control_create_mach_voucher(
2381 ipc_voucher_attr_control_t control
,
2382 mach_voucher_attr_raw_recipe_array_t recipes
,
2383 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2384 ipc_voucher_t
*new_voucher
)
2386 mach_voucher_attr_key_t control_key
;
2387 mach_voucher_attr_recipe_t sub_recipe
;
2388 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2389 ipc_voucher_t voucher
= IV_NULL
;
2390 kern_return_t kr
= KERN_SUCCESS
;
2392 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
)
2393 return KERN_INVALID_CAPABILITY
;
2395 /* if nothing to do ... */
2396 if (0 == recipe_size
) {
2397 *new_voucher
= IV_NULL
;
2398 return KERN_SUCCESS
;
2401 /* allocate new voucher */
2402 voucher
= iv_alloc(ivgt_keys_in_use
);
2403 if (IV_NULL
== voucher
)
2404 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
)
2445 if (KERN_SUCCESS
== kr
) {
2446 *new_voucher
= iv_dedup(voucher
);
2448 *new_voucher
= IV_NULL
;
2449 iv_dealloc(voucher
, FALSE
);
2455 * Routine: host_create_mach_voucher
2457 * Create a new mach voucher and initialize it by processing the
2458 * supplied recipe(s).
2460 * Comming in from user-space, each recipe item will have a previous
2461 * recipe port name that needs to be converted to a voucher. Because
2462 * we can't rely on the port namespace to hold a reference on each
2463 * previous voucher port for the duration of processing that command,
2464 * we have to convert the name to a voucher reference and release it
2465 * after the command processing is done.
2468 host_create_mach_voucher(
2470 mach_voucher_attr_raw_recipe_array_t recipes
,
2471 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2472 ipc_voucher_t
*new_voucher
)
2474 mach_voucher_attr_recipe_t sub_recipe
;
2475 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2476 ipc_voucher_t voucher
= IV_NULL
;
2477 kern_return_t kr
= KERN_SUCCESS
;
2479 if (host
== HOST_NULL
)
2480 return KERN_INVALID_ARGUMENT
;
2482 /* if nothing to do ... */
2483 if (0 == recipe_size
) {
2484 *new_voucher
= IV_NULL
;
2485 return KERN_SUCCESS
;
2488 /* allocate new voucher */
2489 voucher
= iv_alloc(ivgt_keys_in_use
);
2490 if (IV_NULL
== voucher
)
2491 return KERN_RESOURCE_SHORTAGE
;
2493 /* iterate over the recipe items */
2494 while (0 < recipe_size
- recipe_used
) {
2495 ipc_voucher_t prev_iv
;
2497 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2498 kr
= KERN_INVALID_ARGUMENT
;
2502 /* find the next recipe */
2503 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2504 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2505 kr
= KERN_INVALID_ARGUMENT
;
2508 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2510 /* convert voucher port name (current space) into a voucher reference */
2511 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2512 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2513 kr
= KERN_INVALID_CAPABILITY
;
2517 kr
= ipc_execute_voucher_recipe_command(voucher
,
2519 sub_recipe
->command
,
2521 sub_recipe
->content
,
2522 sub_recipe
->content_size
,
2524 ipc_voucher_release(prev_iv
);
2526 if (KERN_SUCCESS
!= kr
)
2530 if (KERN_SUCCESS
== kr
) {
2531 *new_voucher
= iv_dedup(voucher
);
2533 *new_voucher
= IV_NULL
;
2534 iv_dealloc(voucher
, FALSE
);
2540 * Routine: host_register_well_known_mach_voucher_attr_manager
2542 * Register the user-level resource manager responsible for a given
2545 * The manager port passed in has to be converted/wrapped
2546 * in an ipc_voucher_attr_manager_t structure and then call the
2547 * internal variant. We have a generic ipc voucher manager
2548 * type that implements a MIG proxy out to user-space just for
2552 host_register_well_known_mach_voucher_attr_manager(
2554 mach_voucher_attr_manager_t __unused manager
,
2555 mach_voucher_attr_value_handle_t __unused default_value
,
2556 mach_voucher_attr_key_t __unused key
,
2557 ipc_voucher_attr_control_t __unused
*control
)
2559 if (HOST_NULL
== host
)
2560 return KERN_INVALID_HOST
;
2563 return KERN_NOT_SUPPORTED
;
2566 * Allocate a mig_voucher_attr_manager_t that provides the
2567 * MIG proxy functions for the three manager callbacks and
2568 * store the port right in there.
2570 * If the user-space manager dies, we'll detect it on our
2571 * next upcall, and cleanup the proxy at that point.
2573 mig_voucher_attr_manager_t proxy
;
2576 proxy
= mvam_alloc(manager
);
2578 kr
= ipc_register_well_known_mach_voucher_attr_manager(&proxy
->mvam_manager
,
2582 if (KERN_SUCCESS
!= kr
)
2583 mvam_release(proxy
);
2590 * Routine: host_register_mach_voucher_attr_manager
2592 * Register the user-space resource manager and return a
2593 * dynamically allocated key.
2595 * Wrap the supplied port with the MIG proxy ipc
2596 * voucher resource manager, and then call the internal
2600 host_register_mach_voucher_attr_manager(
2602 mach_voucher_attr_manager_t __unused manager
,
2603 mach_voucher_attr_value_handle_t __unused default_value
,
2604 mach_voucher_attr_key_t __unused
*key
,
2605 ipc_voucher_attr_control_t __unused
*control
)
2607 if (HOST_NULL
== host
)
2608 return KERN_INVALID_HOST
;
2610 return KERN_NOT_SUPPORTED
;
2614 * Routine: ipc_get_pthpriority_from_kmsg_voucher
2616 * Get the canonicalized pthread priority from the voucher attached in the kmsg.
2619 ipc_get_pthpriority_from_kmsg_voucher(
2621 ipc_pthread_priority_value_t
*canonicalize_priority_value
)
2623 ipc_voucher_t pthread_priority_voucher
;
2624 mach_voucher_attr_raw_recipe_size_t content_size
=
2625 sizeof(mach_voucher_attr_recipe_data_t
) + sizeof(ipc_pthread_priority_value_t
);
2626 uint8_t content_data
[content_size
];
2627 mach_voucher_attr_recipe_t cur_content
;
2628 kern_return_t kr
= KERN_SUCCESS
;
2630 if (!IP_VALID(kmsg
->ikm_voucher
)) {
2631 return KERN_FAILURE
;
2634 pthread_priority_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2635 kr
= mach_voucher_extract_attr_recipe(pthread_priority_voucher
,
2636 MACH_VOUCHER_ATTR_KEY_PTHPRIORITY
,
2639 if (kr
!= KERN_SUCCESS
) {
2643 /* return KERN_INVALID_VALUE for default value */
2644 if (content_size
< sizeof(mach_voucher_attr_recipe_t
)) {
2645 return KERN_INVALID_VALUE
;
2648 cur_content
= (mach_voucher_attr_recipe_t
) (void *) &content_data
[0];
2649 assert(cur_content
->content_size
== sizeof(ipc_pthread_priority_value_t
));
2650 memcpy(canonicalize_priority_value
, cur_content
->content
, sizeof(ipc_pthread_priority_value_t
));
2652 return KERN_SUCCESS
;
2657 * Routine: ipc_voucher_send_preprocessing
2659 * Processing of the voucher in the kmsg before sending it.
2660 * Currently use to switch PERSONA_TOKEN in case of process with
2661 * no com.apple.private.personas.propagate entitlement.
2664 ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg
)
2666 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2667 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2668 sizeof(ipc_voucher_attr_recipe_data_t
);
2669 ipc_voucher_t pre_processed_voucher
;
2670 ipc_voucher_t voucher_to_send
;
2672 int need_preprocessing
= FALSE
;
2674 if (!IP_VALID(kmsg
->ikm_voucher
) || current_task() == kernel_task
) {
2678 /* setup recipe for preprocessing of all the attributes. */
2679 pre_processed_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2681 kr
= ipc_voucher_prepare_processing_recipe(pre_processed_voucher
,
2682 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2683 &recipe_size
, MACH_VOUCHER_ATTR_SEND_PREPROCESS
,
2684 IVAM_FLAGS_SUPPORT_SEND_PREPROCESS
, &need_preprocessing
);
2686 assert(KERN_SUCCESS
== kr
);
2688 * Only do send preprocessing if the voucher needs any pre processing.
2690 if (need_preprocessing
) {
2691 kr
= ipc_create_mach_voucher(recipes
,
2694 assert(KERN_SUCCESS
== kr
);
2695 ipc_port_release_send(kmsg
->ikm_voucher
);
2696 kmsg
->ikm_voucher
= convert_voucher_to_port(voucher_to_send
);
2701 * Routine: ipc_voucher_receive_postprocessing
2703 * Redeems the voucher attached to the kmsg.
2705 * Although it is possible to call ipc_importance_receive
2706 * here, it is called in mach_msg_receive_results and not here
2707 * in order to maintain symmetry with ipc_voucher_send_preprocessing.
2710 ipc_voucher_receive_postprocessing(
2712 mach_msg_option_t option
)
2714 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2715 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2716 sizeof(ipc_voucher_attr_recipe_data_t
);
2717 ipc_voucher_t recv_voucher
;
2718 ipc_voucher_t sent_voucher
;
2720 int need_postprocessing
= FALSE
;
2722 if ((option
& MACH_RCV_VOUCHER
) == 0 || (!IP_VALID(kmsg
->ikm_voucher
)) ||
2723 current_task() == kernel_task
) {
2727 /* setup recipe for auto redeem of all the attributes. */
2728 sent_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2730 kr
= ipc_voucher_prepare_processing_recipe(sent_voucher
,
2731 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2732 &recipe_size
, MACH_VOUCHER_ATTR_AUTO_REDEEM
,
2733 IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS
, &need_postprocessing
);
2735 assert(KERN_SUCCESS
== kr
);
2738 * Only do receive postprocessing if the voucher needs any post processing.
2740 if (need_postprocessing
) {
2741 kr
= ipc_create_mach_voucher(recipes
,
2744 assert(KERN_SUCCESS
== kr
);
2745 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
2746 kmsg
->ikm_header
->msgh_bits
|= (MACH_MSG_TYPE_MOVE_SEND
<< 16);
2747 ipc_port_release_send(kmsg
->ikm_voucher
);
2748 kmsg
->ikm_voucher
= convert_voucher_to_port(recv_voucher
);
2753 * Routine: ipc_voucher_prepare_processing_recipe
2755 * Check if the given voucher has an attribute which supports
2756 * the given flag and prepare a recipe to apply that supported
2759 static kern_return_t
2760 ipc_voucher_prepare_processing_recipe(
2761 ipc_voucher_t voucher
,
2762 ipc_voucher_attr_raw_recipe_array_t recipes
,
2763 ipc_voucher_attr_raw_recipe_array_size_t
*in_out_size
,
2764 mach_voucher_attr_recipe_command_t command
,
2765 ipc_voucher_attr_manager_flags flags
,
2766 int *need_processing
)
2768 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= *in_out_size
;
2769 ipc_voucher_attr_raw_recipe_array_size_t recipe_used
= 0;
2770 iv_index_t key_index
;
2771 ipc_voucher_attr_recipe_t recipe
;
2773 if (IV_NULL
== voucher
)
2774 return KERN_INVALID_ARGUMENT
;
2776 /* Setup a recipe to copy all attributes. */
2777 if (recipe_size
< sizeof(*recipe
))
2778 return KERN_NO_SPACE
;
2780 *need_processing
= FALSE
;
2781 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2782 recipe
->key
= MACH_VOUCHER_ATTR_KEY_ALL
;
2783 recipe
->command
= MACH_VOUCHER_ATTR_COPY
;
2784 recipe
->previous_voucher
= voucher
;
2785 recipe
->content_size
= 0;
2786 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2788 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2789 ipc_voucher_attr_manager_t manager
;
2790 mach_voucher_attr_key_t key
;
2791 iv_index_t value_index
;
2793 /* don't output anything for a default value */
2794 value_index
= iv_lookup(voucher
, key_index
);
2795 if (IV_UNUSED_VALINDEX
== value_index
)
2798 if (recipe_size
- recipe_used
< sizeof(*recipe
))
2799 return KERN_NO_SPACE
;
2801 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2804 * Get the manager for this key_index. The
2805 * existence of a non-default value for this
2806 * slot within our voucher will keep the
2807 * manager referenced during the callout.
2809 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2810 assert(IVAM_NULL
!= manager
);
2811 if (IVAM_NULL
== manager
) {
2815 /* Check if the supported flag is set in the manager */
2816 if ((manager
->ivam_flags
& flags
) == 0)
2819 key
= iv_index_to_key(key_index
);
2822 recipe
->command
= command
;
2823 recipe
->content_size
= 0;
2824 recipe
->previous_voucher
= voucher
;
2826 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2827 *need_processing
= TRUE
;
2830 *in_out_size
= recipe_used
;
2831 return KERN_SUCCESS
;
2835 * Activity id Generation
2837 uint64_t voucher_activity_id
;
2839 #define generate_activity_id(x) \
2840 ((uint64_t)OSAddAtomic64((x), (int64_t *)&voucher_activity_id))
2843 * Routine: mach_init_activity_id
2845 * Initialize voucher activity id.
2848 mach_init_activity_id(void)
2850 voucher_activity_id
= 1;
2854 * Routine: mach_generate_activity_id
2856 * Generate a system wide voucher activity id.
2859 mach_generate_activity_id(
2860 struct mach_generate_activity_id_args
*args
)
2862 uint64_t activity_id
;
2863 kern_return_t kr
= KERN_SUCCESS
;
2865 if (args
->count
<= 0 || args
->count
> MACH_ACTIVITY_ID_COUNT_MAX
) {
2866 return KERN_INVALID_ARGUMENT
;
2869 activity_id
= generate_activity_id(args
->count
);
2870 kr
= copyout(&activity_id
, args
->activity_id
, sizeof (activity_id
));
2875 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2878 * Build-in a simple User Data Resource Manager
2880 #define USER_DATA_MAX_DATA (16*1024)
2882 struct user_data_value_element
{
2883 mach_voucher_attr_value_reference_t e_made
;
2884 mach_voucher_attr_content_size_t e_size
;
2887 queue_chain_t e_hash_link
;
2891 typedef struct user_data_value_element
*user_data_element_t
;
2894 * User Data Voucher Hash Table
2896 #define USER_DATA_HASH_BUCKETS 127
2897 #define USER_DATA_HASH_BUCKET(x) ((x) % USER_DATA_HASH_BUCKETS)
2899 static queue_head_t user_data_bucket
[USER_DATA_HASH_BUCKETS
];
2900 static lck_spin_t user_data_lock_data
;
2902 #define user_data_lock_init() \
2903 lck_spin_init(&user_data_lock_data, &ipc_lck_grp, &ipc_lck_attr)
2904 #define user_data_lock_destroy() \
2905 lck_spin_destroy(&user_data_lock_data, &ipc_lck_grp)
2906 #define user_data_lock() \
2907 lck_spin_lock(&user_data_lock_data)
2908 #define user_data_lock_try() \
2909 lck_spin_try_lock(&user_data_lock_data)
2910 #define user_data_unlock() \
2911 lck_spin_unlock(&user_data_lock_data)
2913 static kern_return_t
2914 user_data_release_value(
2915 ipc_voucher_attr_manager_t manager
,
2916 mach_voucher_attr_key_t key
,
2917 mach_voucher_attr_value_handle_t value
,
2918 mach_voucher_attr_value_reference_t sync
);
2920 static kern_return_t
2921 user_data_get_value(
2922 ipc_voucher_attr_manager_t manager
,
2923 mach_voucher_attr_key_t key
,
2924 mach_voucher_attr_recipe_command_t command
,
2925 mach_voucher_attr_value_handle_array_t prev_values
,
2926 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
2927 mach_voucher_attr_content_t content
,
2928 mach_voucher_attr_content_size_t content_size
,
2929 mach_voucher_attr_value_handle_t
*out_value
,
2930 mach_voucher_attr_value_flags_t
*out_flags
,
2931 ipc_voucher_t
*out_value_voucher
);
2933 static kern_return_t
2934 user_data_extract_content(
2935 ipc_voucher_attr_manager_t manager
,
2936 mach_voucher_attr_key_t key
,
2937 mach_voucher_attr_value_handle_array_t values
,
2938 mach_voucher_attr_value_handle_array_size_t value_count
,
2939 mach_voucher_attr_recipe_command_t
*out_command
,
2940 mach_voucher_attr_content_t out_content
,
2941 mach_voucher_attr_content_size_t
*in_out_content_size
);
2943 static kern_return_t
2945 ipc_voucher_attr_manager_t manager
,
2946 mach_voucher_attr_key_t key
,
2947 mach_voucher_attr_value_handle_array_t values
,
2948 mach_msg_type_number_t value_count
,
2949 mach_voucher_attr_command_t command
,
2950 mach_voucher_attr_content_t in_content
,
2951 mach_voucher_attr_content_size_t in_content_size
,
2952 mach_voucher_attr_content_t out_content
,
2953 mach_voucher_attr_content_size_t
*out_content_size
);
2957 ipc_voucher_attr_manager_t manager
);
2959 struct ipc_voucher_attr_manager user_data_manager
= {
2960 .ivam_release_value
= user_data_release_value
,
2961 .ivam_get_value
= user_data_get_value
,
2962 .ivam_extract_content
= user_data_extract_content
,
2963 .ivam_command
= user_data_command
,
2964 .ivam_release
= user_data_release
,
2965 .ivam_flags
= IVAM_FLAGS_NONE
,
2968 ipc_voucher_attr_control_t user_data_control
;
2969 ipc_voucher_attr_control_t test_control
;
2971 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) && defined(MACH_VOUCHER_ATTR_KEY_TEST)
2972 #define USER_DATA_ASSERT_KEY(key) \
2973 assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key) || \
2974 MACH_VOUCHER_ATTR_KEY_TEST == (key));
2975 #elif defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
2976 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key))
2978 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_TEST == (key))
2982 * Routine: user_data_release_value
2984 * Release a made reference on a specific value managed by
2985 * this voucher attribute manager.
2987 * Must remove the element associated with this value from
2988 * the hash if this is the last know made reference.
2990 static kern_return_t
2991 user_data_release_value(
2992 ipc_voucher_attr_manager_t __assert_only manager
,
2993 mach_voucher_attr_key_t __assert_only key
,
2994 mach_voucher_attr_value_handle_t value
,
2995 mach_voucher_attr_value_reference_t sync
)
2997 user_data_element_t elem
;
3000 assert (&user_data_manager
== manager
);
3001 USER_DATA_ASSERT_KEY(key
);
3003 elem
= (user_data_element_t
)value
;
3004 hash
= elem
->e_hash
;
3007 if (sync
== elem
->e_made
) {
3008 queue_remove(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
);
3010 kfree(elem
, sizeof(*elem
) + elem
->e_size
);
3011 return KERN_SUCCESS
;
3013 assert(sync
< elem
->e_made
);
3016 return KERN_FAILURE
;
3020 * Routine: user_data_checksum
3022 * Provide a rudimentary checksum for the data presented
3023 * to these voucher attribute managers.
3027 mach_voucher_attr_content_t content
,
3028 mach_voucher_attr_content_size_t content_size
)
3030 mach_voucher_attr_content_size_t i
;
3031 iv_index_t cksum
= 0;
3033 for(i
= 0; i
< content_size
; i
++, content
++) {
3034 cksum
= (cksum
<< 8) ^ (cksum
+ *(unsigned char *)content
);
3041 * Routine: user_data_dedup
3043 * See if the content represented by this request already exists
3044 * in another user data element. If so return a made reference
3045 * to the existing element. Otherwise, create a new element and
3046 * return that (after inserting it in the hash).
3050 * A made reference on the user_data_element_t
3052 static user_data_element_t
3054 mach_voucher_attr_content_t content
,
3055 mach_voucher_attr_content_size_t content_size
)
3059 user_data_element_t elem
;
3060 user_data_element_t alloc
= NULL
;
3062 sum
= user_data_checksum(content
, content_size
);
3063 hash
= USER_DATA_HASH_BUCKET(sum
);
3067 queue_iterate(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
) {
3068 assert(elem
->e_hash
== hash
);
3070 /* if sums match... */
3071 if (elem
->e_sum
== sum
&& elem
->e_size
== content_size
) {
3074 /* and all data matches */
3075 for (i
= 0; i
< content_size
; i
++)
3076 if (elem
->e_data
[i
] != content
[i
])
3078 if (i
< content_size
)
3081 /* ... we found a match... */
3087 kfree(alloc
, sizeof(*alloc
) + content_size
);
3093 if (NULL
== alloc
) {
3096 alloc
= (user_data_element_t
)kalloc(sizeof(*alloc
) + content_size
);
3098 alloc
->e_size
= content_size
;
3100 alloc
->e_hash
= hash
;
3101 memcpy(alloc
->e_data
, content
, content_size
);
3105 queue_enter(&user_data_bucket
[hash
], alloc
, user_data_element_t
, e_hash_link
);
3111 static kern_return_t
3112 user_data_get_value(
3113 ipc_voucher_attr_manager_t __assert_only manager
,
3114 mach_voucher_attr_key_t __assert_only key
,
3115 mach_voucher_attr_recipe_command_t command
,
3116 mach_voucher_attr_value_handle_array_t prev_values
,
3117 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3118 mach_voucher_attr_content_t content
,
3119 mach_voucher_attr_content_size_t content_size
,
3120 mach_voucher_attr_value_handle_t
*out_value
,
3121 mach_voucher_attr_value_flags_t
*out_flags
,
3122 ipc_voucher_t
*out_value_voucher
)
3124 user_data_element_t elem
;
3126 assert (&user_data_manager
== manager
);
3127 USER_DATA_ASSERT_KEY(key
);
3129 /* never an out voucher */
3130 *out_value_voucher
= IPC_VOUCHER_NULL
;
3131 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
3135 case MACH_VOUCHER_ATTR_REDEEM
:
3137 /* redeem of previous values is the value */
3138 if (0 < prev_value_count
) {
3139 elem
= (user_data_element_t
)prev_values
[0];
3140 assert(0 < elem
->e_made
);
3142 *out_value
= prev_values
[0];
3143 return KERN_SUCCESS
;
3146 /* redeem of default is default */
3148 return KERN_SUCCESS
;
3150 case MACH_VOUCHER_ATTR_USER_DATA_STORE
:
3151 if (USER_DATA_MAX_DATA
< content_size
)
3152 return KERN_RESOURCE_SHORTAGE
;
3154 /* empty is the default */
3155 if (0 == content_size
) {
3157 return KERN_SUCCESS
;
3160 elem
= user_data_dedup(content
, content_size
);
3161 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
3162 return KERN_SUCCESS
;
3165 /* every other command is unknown */
3166 return KERN_INVALID_ARGUMENT
;
3170 static kern_return_t
3171 user_data_extract_content(
3172 ipc_voucher_attr_manager_t __assert_only manager
,
3173 mach_voucher_attr_key_t __assert_only key
,
3174 mach_voucher_attr_value_handle_array_t values
,
3175 mach_voucher_attr_value_handle_array_size_t value_count
,
3176 mach_voucher_attr_recipe_command_t
*out_command
,
3177 mach_voucher_attr_content_t out_content
,
3178 mach_voucher_attr_content_size_t
*in_out_content_size
)
3180 mach_voucher_attr_content_size_t size
= 0;
3181 user_data_element_t elem
;
3184 assert (&user_data_manager
== manager
);
3185 USER_DATA_ASSERT_KEY(key
);
3187 /* concatenate the stored data items */
3188 for (i
= 0; i
< value_count
; i
++) {
3189 elem
= (user_data_element_t
)values
[i
];
3190 assert(USER_DATA_MAX_DATA
>= elem
->e_size
);
3192 if (size
+ elem
->e_size
> *in_out_content_size
)
3193 return KERN_NO_SPACE
;
3195 memcpy(&out_content
[size
], elem
->e_data
, elem
->e_size
);
3196 size
+= elem
->e_size
;
3198 *out_command
= MACH_VOUCHER_ATTR_BITS_STORE
;
3199 *in_out_content_size
= size
;
3200 return KERN_SUCCESS
;
3203 static kern_return_t
3205 ipc_voucher_attr_manager_t __assert_only manager
,
3206 mach_voucher_attr_key_t __assert_only key
,
3207 mach_voucher_attr_value_handle_array_t __unused values
,
3208 mach_msg_type_number_t __unused value_count
,
3209 mach_voucher_attr_command_t __unused command
,
3210 mach_voucher_attr_content_t __unused in_content
,
3211 mach_voucher_attr_content_size_t __unused in_content_size
,
3212 mach_voucher_attr_content_t __unused out_content
,
3213 mach_voucher_attr_content_size_t __unused
*out_content_size
)
3215 assert (&user_data_manager
== manager
);
3216 USER_DATA_ASSERT_KEY(key
);
3217 return KERN_FAILURE
;
3222 ipc_voucher_attr_manager_t manager
)
3224 if (manager
!= &user_data_manager
)
3227 panic("Voucher user-data manager released");
3230 static int user_data_manager_inited
= 0;
3233 user_data_attr_manager_init()
3237 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
3238 if ((user_data_manager_inited
& 0x1) != 0x1) {
3239 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3240 (mach_voucher_attr_value_handle_t
)0,
3241 MACH_VOUCHER_ATTR_KEY_USER_DATA
,
3242 &user_data_control
);
3243 if (KERN_SUCCESS
!= kr
)
3244 printf("Voucher user-data manager register(USER-DATA) returned %d", kr
);
3246 user_data_manager_inited
|= 0x1;
3249 #if defined(MACH_VOUCHER_ATTR_KEY_TEST)
3250 if ((user_data_manager_inited
& 0x2) != 0x2) {
3251 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3252 (mach_voucher_attr_value_handle_t
)0,
3253 MACH_VOUCHER_ATTR_KEY_TEST
,
3255 if (KERN_SUCCESS
!= kr
)
3256 printf("Voucher user-data manager register(TEST) returned %d", kr
);
3258 user_data_manager_inited
|= 0x2;
3261 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
3264 for (i
=0; i
< USER_DATA_HASH_BUCKETS
; i
++)
3265 queue_init(&user_data_bucket
[i
]);
3267 user_data_lock_init();
3271 #endif /* MACH_DEBUG */