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/notify.h>
31 #include <ipc/ipc_types.h>
32 #include <ipc/ipc_port.h>
33 #include <ipc/ipc_voucher.h>
34 #include <kern/ipc_kobject.h>
35 #include <kern/ipc_tt.h>
36 #include <kern/mach_param.h>
37 #include <kern/kalloc.h>
38 #include <kern/zalloc.h>
40 #include <libkern/OSAtomic.h>
42 #include <mach/mach_voucher_server.h>
43 #include <mach/mach_voucher_attr_control_server.h>
44 #include <mach/mach_host_server.h>
47 * Sysctl variable; enable and disable tracing of voucher contents
49 uint32_t ipc_voucher_trace_contents
= 0;
51 static zone_t ipc_voucher_zone
;
52 static zone_t ipc_voucher_attr_control_zone
;
57 #define IV_HASH_BUCKETS 127
58 #define IV_HASH_BUCKET(x) ((x) % IV_HASH_BUCKETS)
60 static queue_head_t ivht_bucket
[IV_HASH_BUCKETS
];
61 static lck_spin_t ivht_lock_data
;
62 static uint32_t ivht_count
= 0;
64 #define ivht_lock_init() \
65 lck_spin_init(&ivht_lock_data, &ipc_lck_grp, &ipc_lck_attr)
66 #define ivht_lock_destroy() \
67 lck_spin_destroy(&ivht_lock_data, &ipc_lck_grp)
69 lck_spin_lock(&ivht_lock_data)
70 #define ivht_lock_try() \
71 lck_spin_try_lock(&ivht_lock_data)
72 #define ivht_unlock() \
73 lck_spin_unlock(&ivht_lock_data)
76 * Global table of resource manager registrations
78 * NOTE: For now, limited to well-known resource managers
79 * eventually, will include dynamic allocations requiring
80 * table growth and hashing by key.
82 static iv_index_t ivgt_keys_in_use
= MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
;
83 static ipc_voucher_global_table_element iv_global_table
[MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
];
84 static lck_spin_t ivgt_lock_data
;
86 #define ivgt_lock_init() \
87 lck_spin_init(&ivgt_lock_data, &ipc_lck_grp, &ipc_lck_attr)
88 #define ivgt_lock_destroy() \
89 lck_spin_destroy(&ivgt_lock_data, &ipc_lck_grp)
91 lck_spin_lock(&ivgt_lock_data)
92 #define ivgt_lock_try() \
93 lck_spin_try_lock(&ivgt_lock_data)
94 #define ivgt_unlock() \
95 lck_spin_unlock(&ivgt_lock_data)
97 ipc_voucher_t
iv_alloc(iv_index_t entries
);
98 void iv_dealloc(ipc_voucher_t iv
, boolean_t unhash
);
100 static inline iv_refs_t
101 iv_reference(ipc_voucher_t iv
)
105 refs
= hw_atomic_add(&iv
->iv_refs
, 1);
110 iv_release(ipc_voucher_t iv
)
114 assert(0 < iv
->iv_refs
);
115 refs
= hw_atomic_sub(&iv
->iv_refs
, 1);
117 iv_dealloc(iv
, TRUE
);
121 * freelist helper macros
123 #define IV_FREELIST_END ((iv_index_t) 0)
126 * Attribute value hashing helper macros
128 #define IV_HASH_END UINT32_MAX
129 #define IV_HASH_VAL(sz, val) \
130 (((val) >> 3) % (sz))
132 static inline iv_index_t
134 iv_index_t key_index
,
135 mach_voucher_attr_value_handle_t value
)
137 ipc_voucher_attr_control_t ivac
;
139 ivac
= iv_global_table
[key_index
].ivgte_control
;
140 assert(IVAC_NULL
!= ivac
);
141 return IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
145 * Convert a key to an index. This key-index is used to both index
146 * into the voucher table of attribute cache indexes and also the
147 * table of resource managers by key.
149 * For now, well-known keys have a one-to-one mapping of indexes
150 * into these tables. But as time goes on, that may not always
151 * be the case (sparse use over time). This isolates the code from
152 * having to change in these cases - yet still lets us keep a densely
153 * packed set of tables.
155 static inline iv_index_t
156 iv_key_to_index(mach_voucher_attr_key_t key
)
158 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
||
159 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
< key
)
160 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
;
169 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 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
188 void user_data_attr_manager_init(void);
192 ipc_voucher_init(void)
194 natural_t ipc_voucher_max
= (task_max
+ thread_max
) * 2;
195 natural_t attr_manager_max
= MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
;
198 ipc_voucher_zone
= zinit(sizeof(struct ipc_voucher
),
199 ipc_voucher_max
* sizeof(struct ipc_voucher
),
200 sizeof(struct ipc_voucher
),
202 zone_change(ipc_voucher_zone
, Z_NOENCRYPT
, TRUE
);
204 ipc_voucher_attr_control_zone
= zinit(sizeof(struct ipc_voucher_attr_control
),
205 attr_manager_max
* sizeof(struct ipc_voucher_attr_control
),
206 sizeof(struct ipc_voucher_attr_control
),
207 "ipc voucher attr controls");
208 zone_change(ipc_voucher_attr_control_zone
, Z_NOENCRYPT
, TRUE
);
210 /* initialize voucher hash */
212 for (i
= 0; i
< IV_HASH_BUCKETS
; i
++)
213 queue_init(&ivht_bucket
[i
]);
215 /* initialize global table locking */
218 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
219 user_data_attr_manager_init();
224 iv_alloc(iv_index_t entries
)
230 iv
= (ipc_voucher_t
)zalloc(ipc_voucher_zone
);
237 iv
->iv_port
= IP_NULL
;
239 if (entries
> IV_ENTRIES_INLINE
) {
242 /* TODO - switch to ipc_table method of allocation */
243 table
= (iv_entry_t
) kalloc(sizeof(*table
) * entries
);
244 if (IVE_NULL
== table
) {
245 zfree(ipc_voucher_zone
, iv
);
248 iv
->iv_table
= table
;
249 iv
->iv_table_size
= entries
;
251 iv
->iv_table
= iv
->iv_inline_table
;
252 iv
->iv_table_size
= IV_ENTRIES_INLINE
;
255 /* initialize the table entries */
256 for (i
=0; i
< iv
->iv_table_size
; i
++)
257 iv
->iv_table
[i
] = IV_UNUSED_VALINDEX
;
265 * Set the voucher's value index for a given key index.
267 * This is only called during voucher creation, as
268 * they are immutable once references are distributed.
271 iv_set(ipc_voucher_t iv
,
272 iv_index_t key_index
,
273 iv_index_t value_index
)
275 assert(key_index
< iv
->iv_table_size
);
276 iv
->iv_table
[key_index
] = value_index
;
280 iv_dealloc(ipc_voucher_t iv
, boolean_t unhash
)
282 ipc_port_t port
= iv
->iv_port
;
286 * Do we have to remove it from the hash?
290 assert(0 == iv
->iv_refs
);
291 assert(IV_HASH_BUCKETS
> iv
->iv_hash
);
292 queue_remove(&ivht_bucket
[iv
->iv_hash
], iv
, ipc_voucher_t
, iv_hash_link
);
296 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_DESTROY
) | DBG_FUNC_NONE
,
297 VM_KERNEL_ADDRPERM((uintptr_t)iv
), 0, ivht_count
, 0, 0);
300 assert(0 == --iv
->iv_refs
);
303 * if a port was allocated for this voucher,
304 * it must not have any remaining send rights,
305 * because the port's reference on the voucher
306 * is gone. We can just discard it now.
308 if (IP_VALID(port
)) {
309 assert(ip_active(port
));
310 assert(port
->ip_srights
== 0);
312 ipc_port_dealloc_kernel(port
);
315 /* release the attribute references held by this voucher */
316 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
317 ivace_release(i
, iv
->iv_table
[i
]);
323 if (iv
->iv_table
!= iv
->iv_inline_table
)
325 iv
->iv_table_size
* sizeof(*iv
->iv_table
));
327 zfree(ipc_voucher_zone
, iv
);
333 * Find the voucher's value index for a given key_index
335 * Vouchers are immutable, so no locking required to do
338 static inline iv_index_t
339 iv_lookup(ipc_voucher_t iv
, iv_index_t key_index
)
341 if (key_index
< iv
->iv_table_size
)
342 return iv
->iv_table
[key_index
];
343 return IV_UNUSED_VALINDEX
;
347 * Routine: unsafe_convert_port_to_voucher
349 * Unsafe conversion of a port to a voucher.
350 * Intended only for use by trace and debugging
351 * code. Consumes nothing, validates very little,
352 * produces an unreferenced voucher, which you
353 * MAY NOT use as a voucher, only log as an
356 * Caller has a send-right reference to port.
357 * Port may or may not be locked.
360 unsafe_convert_port_to_voucher(
363 if (IP_VALID(port
)) {
364 uintptr_t voucher
= (uintptr_t) port
->ip_kobject
;
367 * No need to lock because we have a reference on the
368 * port, and if it is a true voucher port, that reference
369 * keeps the voucher bound to the port (and active).
371 if (ip_kotype(port
) == IKOT_VOUCHER
)
374 return (uintptr_t)IV_NULL
;
378 * Routine: convert_port_to_voucher
380 * Convert from a port to a voucher.
381 * Doesn't consume the port [send-right] ref;
382 * produces a voucher ref, which may be null.
384 * Caller has a send-right reference to port.
385 * Port may or may not be locked.
388 convert_port_to_voucher(
391 if (IP_VALID(port
)) {
392 ipc_voucher_t voucher
= (ipc_voucher_t
) port
->ip_kobject
;
395 * No need to lock because we have a reference on the
396 * port, and if it is a true voucher port, that reference
397 * keeps the voucher bound to the port (and active).
399 if (ip_kotype(port
) != IKOT_VOUCHER
)
402 assert(ip_active(port
));
404 ipc_voucher_reference(voucher
);
411 * Routine: convert_port_name_to_voucher
413 * Convert from a port name in the current space to a voucher.
414 * Produces a voucher ref, which may be null.
420 convert_port_name_to_voucher(
421 mach_port_name_t voucher_name
)
427 if (MACH_PORT_VALID(voucher_name
)) {
428 kr
= ipc_port_translate_send(current_space(), voucher_name
, &port
);
429 if (KERN_SUCCESS
!= kr
)
432 iv
= convert_port_to_voucher(port
);
441 ipc_voucher_reference(ipc_voucher_t voucher
)
445 if (IPC_VOUCHER_NULL
== voucher
)
448 refs
= iv_reference(voucher
);
453 ipc_voucher_release(ipc_voucher_t voucher
)
455 if (IPC_VOUCHER_NULL
!= voucher
)
460 * Routine: ipc_voucher_notify
462 * Called whenever the Mach port system detects no-senders
463 * on the voucher port.
465 * Each time the send-right count goes positive, a no-senders
466 * notification is armed (and a voucher reference is donated).
467 * So, each notification that comes in must release a voucher
468 * reference. If more send rights have been added since it
469 * fired (asynchronously), they will be protected by a different
473 ipc_voucher_notify(mach_msg_header_t
*msg
)
475 mach_no_senders_notification_t
*notification
= (void *)msg
;
476 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
479 assert(ip_active(port
));
480 assert(IKOT_VOUCHER
== ip_kotype(port
));
481 iv
= (ipc_voucher_t
)port
->ip_kobject
;
483 ipc_voucher_release(iv
);
487 * Convert a voucher to a port.
490 convert_voucher_to_port(ipc_voucher_t voucher
)
492 ipc_port_t port
, send
;
494 if (IV_NULL
== voucher
)
497 assert(0 < voucher
->iv_refs
);
499 /* create a port if needed */
500 port
= voucher
->iv_port
;
501 if (!IP_VALID(port
)) {
502 port
= ipc_port_alloc_kernel();
503 assert(IP_VALID(port
));
504 ipc_kobject_set_atomically(port
, (ipc_kobject_t
) voucher
, IKOT_VOUCHER
);
506 /* If we lose the race, deallocate and pick up the other guy's port */
507 if (!OSCompareAndSwapPtr(IP_NULL
, port
, &voucher
->iv_port
)) {
508 ipc_port_dealloc_kernel(port
);
509 port
= voucher
->iv_port
;
510 assert(ip_kotype(port
) == IKOT_VOUCHER
);
511 assert(port
->ip_kobject
== (ipc_kobject_t
)voucher
);
516 assert(ip_active(port
));
517 send
= ipc_port_make_send_locked(port
);
519 if (1 == port
->ip_srights
) {
520 ipc_port_t old_notify
;
522 /* transfer our ref to the port, and arm the no-senders notification */
523 assert(IP_NULL
== port
->ip_nsrequest
);
524 ipc_port_nsrequest(port
, port
->ip_mscount
, ipc_port_make_sonce_locked(port
), &old_notify
);
526 assert(IP_NULL
== old_notify
);
528 /* piggyback on the existing port reference, so consume ours */
530 ipc_voucher_release(voucher
);
535 #define ivace_reset_data(ivace_elem, next_index) { \
536 (ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE; \
537 (ivace_elem)->ivace_refs = 0; \
538 (ivace_elem)->ivace_made = 0; \
539 (ivace_elem)->ivace_free = TRUE; \
540 (ivace_elem)->ivace_releasing = FALSE; \
541 (ivace_elem)->ivace_layered = 0; \
542 (ivace_elem)->ivace_index = IV_HASH_END; \
543 (ivace_elem)->ivace_next = (next_index); \
546 #define ivace_copy_data(ivace_src_elem, ivace_dst_elem) { \
547 (ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
548 (ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs; \
549 (ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made; \
550 (ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free; \
551 (ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered; \
552 (ivace_dst_elem)->ivace_releasing = (ivace_src_elem)->ivace_releasing; \
553 (ivace_dst_elem)->ivace_index = (ivace_src_elem)->ivace_index; \
554 (ivace_dst_elem)->ivace_next = (ivace_src_elem)->ivace_next; \
557 ipc_voucher_attr_control_t
558 ivac_alloc(iv_index_t key_index
)
560 ipc_voucher_attr_control_t ivac
;
565 ivac
= (ipc_voucher_attr_control_t
)zalloc(ipc_voucher_attr_control_zone
);
566 if (IVAC_NULL
== ivac
)
570 ivac
->ivac_is_growing
= FALSE
;
571 ivac
->ivac_port
= IP_NULL
;
573 /* start with just the inline table */
574 table
= (ivac_entry_t
) kalloc(IVAC_ENTRIES_MIN
* sizeof(ivac_entry
));
575 ivac
->ivac_table
= table
;
576 ivac
->ivac_table_size
= IVAC_ENTRIES_MIN
;
577 ivac
->ivac_init_table_size
= IVAC_ENTRIES_MIN
;
578 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
579 ivace_reset_data(&table
[i
], i
+1);
582 /* the default table entry is never on freelist */
583 table
[0].ivace_next
= IV_HASH_END
;
584 table
[0].ivace_free
= FALSE
;
585 table
[i
-1].ivace_next
= IV_FREELIST_END
;
586 ivac
->ivac_freelist
= 1;
587 ivac_lock_init(ivac
);
588 ivac
->ivac_key_index
= key_index
;
594 ivac_dealloc(ipc_voucher_attr_control_t ivac
)
596 ipc_voucher_attr_manager_t ivam
= IVAM_NULL
;
597 iv_index_t key_index
= ivac
->ivac_key_index
;
598 ipc_port_t port
= ivac
->ivac_port
;
602 * If the control is in the global table, we
603 * have to remove it from there before we (re)confirm
604 * that the reference count is still zero.
607 if (ivac
->ivac_refs
> 0) {
612 /* take it out of the global table */
613 if (iv_global_table
[key_index
].ivgte_control
== ivac
) {
614 ivam
= iv_global_table
[key_index
].ivgte_manager
;
615 iv_global_table
[key_index
].ivgte_manager
= IVAM_NULL
;
616 iv_global_table
[key_index
].ivgte_control
= IVAC_NULL
;
617 iv_global_table
[key_index
].ivgte_key
= MACH_VOUCHER_ATTR_KEY_NONE
;
621 /* release the reference held on the resource manager */
622 if (IVAM_NULL
!= ivam
)
623 (ivam
->ivam_release
)(ivam
);
626 * if a port was allocated for this voucher,
627 * it must not have any remaining send rights,
628 * because the port's reference on the voucher
629 * is gone. We can just discard it now.
631 if (IP_VALID(port
)) {
632 assert(ip_active(port
));
633 assert(port
->ip_srights
== 0);
635 ipc_port_dealloc_kernel(port
);
639 * the resource manager's control reference and all references
640 * held by the specific value caches are gone, so free the
644 for (i
= 0; i
< ivac
->ivac_table_size
; i
++)
645 if (ivac
->ivac_table
[i
].ivace_refs
!= 0)
646 panic("deallocing a resource manager with live refs to its attr values\n");
648 kfree(ivac
->ivac_table
, ivac
->ivac_table_size
* sizeof(*ivac
->ivac_table
));
649 ivac_lock_destroy(ivac
);
650 zfree(ipc_voucher_attr_control_zone
, ivac
);
654 ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control
)
656 ivac_reference(control
);
660 ipc_voucher_attr_control_release(ipc_voucher_attr_control_t control
)
662 ivac_release(control
);
666 * Routine: convert_port_to_voucher_attr_control reference
668 * Convert from a port to a voucher attribute control.
669 * Doesn't consume the port ref; produces a voucher ref,
674 ipc_voucher_attr_control_t
675 convert_port_to_voucher_attr_control(
678 if (IP_VALID(port
)) {
679 ipc_voucher_attr_control_t ivac
= (ipc_voucher_attr_control_t
) port
->ip_kobject
;
682 * No need to lock because we have a reference on the
683 * port, and if it is a true voucher control port,
684 * that reference keeps the voucher bound to the port
687 if (ip_kotype(port
) != IKOT_VOUCHER_ATTR_CONTROL
)
690 assert(ip_active(port
));
692 ivac_reference(ivac
);
699 ipc_voucher_attr_control_notify(mach_msg_header_t
*msg
)
701 mach_no_senders_notification_t
*notification
= (void *)msg
;
702 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
703 ipc_voucher_attr_control_t ivac
;
705 assert(IKOT_VOUCHER_ATTR_CONTROL
== ip_kotype(port
));
707 assert(ip_active(port
));
709 /* if no new send rights, drop a control reference */
710 if (port
->ip_mscount
== notification
->not_count
) {
711 ivac
= (ipc_voucher_attr_control_t
)port
->ip_kobject
;
720 * Convert a voucher attr control to a port.
723 convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control
)
725 ipc_port_t port
, send
;
727 if (IVAC_NULL
== control
)
730 /* create a port if needed */
731 port
= control
->ivac_port
;
732 if (!IP_VALID(port
)) {
733 port
= ipc_port_alloc_kernel();
734 assert(IP_VALID(port
));
735 if (OSCompareAndSwapPtr(IP_NULL
, port
, &control
->ivac_port
)) {
737 ipc_kobject_set_atomically(port
, (ipc_kobject_t
) control
, IKOT_VOUCHER_ATTR_CONTROL
);
739 ipc_port_dealloc_kernel(port
);
740 port
= control
->ivac_port
;
742 assert(ip_kotype(port
) == IKOT_VOUCHER_ATTR_CONTROL
);
743 assert(port
->ip_kobject
== (ipc_kobject_t
)control
);
748 assert(ip_active(port
));
749 send
= ipc_port_make_send_locked(port
);
751 if (1 == port
->ip_srights
) {
752 ipc_port_t old_notify
;
754 /* transfer our ref to the port, and arm the no-senders notification */
755 assert(IP_NULL
== port
->ip_nsrequest
);
756 ipc_port_nsrequest(port
, port
->ip_mscount
, ipc_port_make_sonce_locked(port
), &old_notify
);
757 assert(IP_NULL
== old_notify
);
760 /* piggyback on the existing port reference, so consume ours */
762 ivac_release(control
);
768 * Look up the values for a given <key, index> pair.
772 iv_index_t key_index
,
773 iv_index_t value_index
,
774 mach_voucher_attr_value_handle_array_t values
,
775 mach_voucher_attr_value_handle_array_size_t
*count
)
777 ipc_voucher_attr_control_t ivac
;
780 if (IV_UNUSED_VALINDEX
== value_index
||
781 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
<= key_index
) {
786 ivac
= iv_global_table
[key_index
].ivgte_control
;
787 assert(IVAC_NULL
!= ivac
);
790 * Get the entry and then the linked values.
793 assert(value_index
< ivac
->ivac_table_size
);
794 ivace
= &ivac
->ivac_table
[value_index
];
797 * TODO: support chained values (for effective vouchers).
799 assert(ivace
->ivace_refs
> 0);
800 values
[0] = ivace
->ivace_value
;
806 * ivac_grow_table - Allocate a bigger table of attribute values
808 * Conditions: ivac is locked on entry and again on return
811 ivac_grow_table(ipc_voucher_attr_control_t ivac
)
815 /* NOTE: do not modify *_table and *_size values once set */
816 ivac_entry_t new_table
= NULL
, old_table
= NULL
;
817 iv_index_t new_size
, old_size
;
819 if (ivac
->ivac_is_growing
) {
824 ivac
->ivac_is_growing
= 1;
825 if (ivac
->ivac_table_size
>= IVAC_ENTRIES_MAX
) {
826 panic("Cannot grow ipc space beyond IVAC_ENTRIES_MAX. Some process is leaking vouchers");
829 old_size
= ivac
->ivac_table_size
;
833 * if initial size is not leading to page aligned allocations,
834 * set new_size such that new_size * sizeof(ivac_entry) is page aligned.
837 if ((old_size
* sizeof(ivac_entry
)) & PAGE_MASK
){
838 new_size
= (iv_index_t
)round_page((old_size
* sizeof(ivac_entry
)))/(sizeof (ivac_entry
));
840 new_size
= old_size
* 2;
843 assert(new_size
> old_size
);
844 new_table
= kalloc(sizeof(ivac_entry
) * new_size
);
846 panic("Failed to grow ivac table to size %d\n", new_size
);
850 /* setup the free list for new entries */
851 for (i
= old_size
; i
< new_size
; i
++) {
852 ivace_reset_data(&new_table
[i
], i
+1);
857 for (i
= 0; i
< ivac
->ivac_table_size
; i
++){
858 ivace_copy_data(&ivac
->ivac_table
[i
], &new_table
[i
]);
861 old_table
= ivac
->ivac_table
;
863 ivac
->ivac_table
= new_table
;
864 ivac
->ivac_table_size
= new_size
;
866 /* adding new free entries at head of freelist */
867 ivac
->ivac_table
[new_size
- 1].ivace_next
= ivac
->ivac_freelist
;
868 ivac
->ivac_freelist
= old_size
;
869 ivac
->ivac_is_growing
= 0;
874 kfree(old_table
, old_size
* sizeof(ivac_entry
));
880 * ivace_reference_by_index
882 * Take an additional reference on the <key_index, val_index>
883 * cached value. It is assumed the caller already holds a
884 * reference to the same cached key-value pair.
887 ivace_reference_by_index(
888 iv_index_t key_index
,
889 iv_index_t val_index
)
891 ipc_voucher_attr_control_t ivac
;
894 if (IV_UNUSED_VALINDEX
== val_index
)
897 ivgt_lookup(key_index
, FALSE
, NULL
, &ivac
);
898 assert(IVAC_NULL
!= ivac
);
901 assert(val_index
< ivac
->ivac_table_size
);
902 ivace
= &ivac
->ivac_table
[val_index
];
904 assert(0xdeadc0dedeadc0de != ivace
->ivace_value
);
905 assert(0 < ivace
->ivace_refs
);
906 assert(!ivace
->ivace_free
);
913 * Look up the values for a given <key, index> pair.
915 * Consumes a reference on the passed voucher control.
916 * Either it is donated to a newly-created value cache
917 * or it is released (if we piggy back on an existing
918 * value cache entry).
921 ivace_reference_by_value(
922 ipc_voucher_attr_control_t ivac
,
923 mach_voucher_attr_value_handle_t value
)
925 ivac_entry_t ivace
= IVACE_NULL
;
926 iv_index_t hash_index
;
929 if (IVAC_NULL
== ivac
) {
930 return IV_UNUSED_VALINDEX
;
935 hash_index
= IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
936 index
= ivac
->ivac_table
[hash_index
].ivace_index
;
937 while (index
!= IV_HASH_END
) {
938 assert(index
< ivac
->ivac_table_size
);
939 ivace
= &ivac
->ivac_table
[index
];
940 assert(!ivace
->ivace_free
);
942 if (ivace
->ivace_value
== value
)
945 assert(ivace
->ivace_next
!= index
);
946 index
= ivace
->ivace_next
;
950 if (index
!= IV_HASH_END
) {
951 /* only add reference on non-default value */
952 if (IV_UNUSED_VALINDEX
!= index
) {
962 /* insert new entry in the table */
963 index
= ivac
->ivac_freelist
;
964 if (IV_FREELIST_END
== index
) {
966 ivac_grow_table(ivac
);
970 /* take the entry off the freelist */
971 ivace
= &ivac
->ivac_table
[index
];
972 ivac
->ivac_freelist
= ivace
->ivace_next
;
974 /* initialize the new entry */
975 ivace
->ivace_value
= value
;
976 ivace
->ivace_refs
= 1;
977 ivace
->ivace_made
= 1;
978 ivace
->ivace_free
= FALSE
;
980 /* insert the new entry in the proper hash chain */
981 ivace
->ivace_next
= ivac
->ivac_table
[hash_index
].ivace_index
;
982 ivac
->ivac_table
[hash_index
].ivace_index
= index
;
985 /* donated passed in ivac reference to new entry */
991 * Release a reference on the given <key_index, value_index> pair.
993 * Conditions: called with nothing locked, as it may cause
994 * callouts and/or messaging to the resource
997 static void ivace_release(
998 iv_index_t key_index
,
999 iv_index_t value_index
)
1001 ipc_voucher_attr_control_t ivac
;
1002 ipc_voucher_attr_manager_t ivam
;
1003 mach_voucher_attr_value_handle_t value
;
1004 mach_voucher_attr_value_reference_t made
;
1005 mach_voucher_attr_key_t key
;
1006 iv_index_t hash_index
;
1010 /* cant release the default value */
1011 if (IV_UNUSED_VALINDEX
== value_index
)
1014 ivgt_lookup(key_index
, FALSE
, &ivam
, &ivac
);
1015 assert(IVAC_NULL
!= ivac
);
1016 assert(IVAM_NULL
!= ivam
);
1019 assert(value_index
< ivac
->ivac_table_size
);
1020 ivace
= &ivac
->ivac_table
[value_index
];
1022 assert(0 < ivace
->ivace_refs
);
1024 if (0 < --ivace
->ivace_refs
) {
1029 key
= iv_index_to_key(key_index
);
1030 assert(MACH_VOUCHER_ATTR_KEY_NONE
!= key
);
1033 * if last return reply is still pending,
1034 * let it handle this later return when
1035 * the previous reply comes in.
1037 if (ivace
->ivace_releasing
) {
1042 /* claim releasing */
1043 ivace
->ivace_releasing
= TRUE
;
1044 value
= ivace
->ivace_value
;
1047 assert(value
== ivace
->ivace_value
);
1048 assert(!ivace
->ivace_free
);
1049 made
= ivace
->ivace_made
;
1052 /* callout to manager's release_value */
1053 kr
= (ivam
->ivam_release_value
)(ivam
, key
, value
, made
);
1055 /* recalculate entry address as table may have changed */
1057 ivace
= &ivac
->ivac_table
[value_index
];
1058 assert(value
== ivace
->ivace_value
);
1061 * new made values raced with this return. If the
1062 * manager OK'ed the prior release, we have to start
1063 * the made numbering over again (pretend the race
1064 * didn't happen). If the entry has zero refs again,
1065 * re-drive the release.
1067 if (ivace
->ivace_made
!= made
) {
1068 assert(made
< ivace
->ivace_made
);
1070 if (KERN_SUCCESS
== kr
)
1071 ivace
->ivace_made
-= made
;
1073 if (0 == ivace
->ivace_refs
)
1076 ivace
->ivace_releasing
= FALSE
;
1081 * If the manager returned FAILURE, someone took a
1082 * reference on the value but have not updated the ivace,
1083 * release the lock and return since thread who got
1084 * the new reference will update the ivace and will have
1085 * non-zero reference on the value.
1087 if (KERN_SUCCESS
!= kr
) {
1088 ivace
->ivace_releasing
= FALSE
;
1094 assert(0 == ivace
->ivace_refs
);
1097 * going away - remove entry from its hash
1098 * If its at the head of the hash bucket list (common), unchain
1099 * at the head. Otherwise walk the chain until the next points
1100 * at this entry, and remove it from the the list there.
1102 hash_index
= iv_hash_value(key_index
, value
);
1103 if (ivac
->ivac_table
[hash_index
].ivace_index
== value_index
) {
1104 ivac
->ivac_table
[hash_index
].ivace_index
= ivace
->ivace_next
;
1106 hash_index
= ivac
->ivac_table
[hash_index
].ivace_index
;
1107 assert(IV_HASH_END
!= hash_index
);
1108 while (ivac
->ivac_table
[hash_index
].ivace_next
!= value_index
) {
1109 hash_index
= ivac
->ivac_table
[hash_index
].ivace_next
;
1110 assert(IV_HASH_END
!= hash_index
);
1112 ivac
->ivac_table
[hash_index
].ivace_next
= ivace
->ivace_next
;
1115 /* Put this entry on the freelist */
1116 ivace
->ivace_value
= 0xdeadc0dedeadc0de;
1117 ivace
->ivace_releasing
= FALSE
;
1118 ivace
->ivace_free
= TRUE
;
1119 ivace
->ivace_made
= 0;
1120 ivace
->ivace_next
= ivac
->ivac_freelist
;
1121 ivac
->ivac_freelist
= value_index
;
1124 /* release the reference this value held on its cache control */
1134 * Lookup an entry in the global table from the context of a manager
1135 * registration. Adds a reference to the control to keep the results
1136 * around (if needed).
1138 * Because of the calling point, we can't be sure the manager is
1139 * [fully] registered yet. So, we must hold the global table lock
1140 * during the lookup to synchronize with in-parallel registrations
1141 * (and possible table growth).
1144 ivgt_lookup(iv_index_t key_index
,
1145 boolean_t take_reference
,
1146 ipc_voucher_attr_manager_t
*manager
,
1147 ipc_voucher_attr_control_t
*control
)
1149 ipc_voucher_attr_control_t ivac
;
1151 if (key_index
< MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
) {
1153 if (NULL
!= manager
)
1154 *manager
= iv_global_table
[key_index
].ivgte_manager
;
1155 ivac
= iv_global_table
[key_index
].ivgte_control
;
1156 if (IVAC_NULL
!= ivac
) {
1157 assert(key_index
== ivac
->ivac_key_index
);
1158 if (take_reference
) {
1159 assert(NULL
!= control
);
1160 ivac_reference(ivac
);
1164 if (NULL
!= control
)
1167 if (NULL
!= manager
)
1168 *manager
= IVAM_NULL
;
1169 if (NULL
!= control
)
1170 *control
= IVAC_NULL
;
1175 * Routine: ipc_replace_voucher_value
1177 * Replace the <voucher, key> value with the results of
1178 * running the supplied command through the resource
1179 * manager's get-value callback.
1181 * Nothing locked (may invoke user-space repeatedly).
1182 * Caller holds references on voucher and previous voucher.
1184 static kern_return_t
1185 ipc_replace_voucher_value(
1186 ipc_voucher_t voucher
,
1187 mach_voucher_attr_key_t key
,
1188 mach_voucher_attr_recipe_command_t command
,
1189 ipc_voucher_t prev_voucher
,
1190 mach_voucher_attr_content_t content
,
1191 mach_voucher_attr_content_size_t content_size
)
1193 mach_voucher_attr_value_handle_t previous_vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1194 mach_voucher_attr_value_handle_array_size_t previous_vals_count
;
1195 mach_voucher_attr_value_handle_t new_value
;
1196 ipc_voucher_t new_value_voucher
;
1197 ipc_voucher_attr_manager_t ivam
;
1198 ipc_voucher_attr_control_t ivac
;
1199 iv_index_t prev_val_index
;
1200 iv_index_t save_val_index
;
1201 iv_index_t val_index
;
1202 iv_index_t key_index
;
1206 * Get the manager for this key_index.
1207 * Returns a reference on the control.
1209 key_index
= iv_key_to_index(key
);
1210 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1211 if (IVAM_NULL
== ivam
)
1212 return KERN_INVALID_ARGUMENT
;
1214 /* save the current value stored in the forming voucher */
1215 save_val_index
= iv_lookup(voucher
, key_index
);
1218 * Get the previous value(s) for this key creation.
1219 * If a previous voucher is specified, they come from there.
1220 * Otherwise, they come from the intermediate values already
1221 * in the forming voucher.
1223 prev_val_index
= (IV_NULL
!= prev_voucher
) ?
1224 iv_lookup(prev_voucher
, key_index
) :
1226 ivace_lookup_values(key_index
, prev_val_index
,
1227 previous_vals
, &previous_vals_count
);
1229 /* Call out to resource manager to get new value */
1230 new_value_voucher
= IV_NULL
;
1231 kr
= (ivam
->ivam_get_value
)(
1233 previous_vals
, previous_vals_count
,
1234 content
, content_size
,
1235 &new_value
, &new_value_voucher
);
1236 if (KERN_SUCCESS
!= kr
) {
1241 /* TODO: value insertion from returned voucher */
1242 if (IV_NULL
!= new_value_voucher
)
1243 iv_release(new_value_voucher
);
1246 * Find or create a slot in the table associated
1247 * with this attribute value. The ivac reference
1248 * is transferred to a new value, or consumed if
1249 * we find a matching existing value.
1251 val_index
= ivace_reference_by_value(ivac
, new_value
);
1252 iv_set(voucher
, key_index
, val_index
);
1255 * release saved old value from the newly forming voucher
1256 * This is saved until the end to avoid churning the
1257 * release logic in cases where the same value is returned
1258 * as was there before.
1260 ivace_release(key_index
, save_val_index
);
1262 return KERN_SUCCESS
;
1266 * Routine: ipc_directly_replace_voucher_value
1268 * Replace the <voucher, key> value with the value-handle
1269 * supplied directly by the attribute manager.
1272 * Caller holds references on voucher.
1273 * A made reference to the value-handle is donated by the caller.
1275 static kern_return_t
1276 ipc_directly_replace_voucher_value(
1277 ipc_voucher_t voucher
,
1278 mach_voucher_attr_key_t key
,
1279 mach_voucher_attr_value_handle_t new_value
)
1281 ipc_voucher_attr_manager_t ivam
;
1282 ipc_voucher_attr_control_t ivac
;
1283 iv_index_t save_val_index
;
1284 iv_index_t val_index
;
1285 iv_index_t key_index
;
1288 * Get the manager for this key_index.
1289 * Returns a reference on the control.
1291 key_index
= iv_key_to_index(key
);
1292 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1293 if (IVAM_NULL
== ivam
)
1294 return KERN_INVALID_ARGUMENT
;
1296 /* save the current value stored in the forming voucher */
1297 save_val_index
= iv_lookup(voucher
, key_index
);
1300 * Find or create a slot in the table associated
1301 * with this attribute value. The ivac reference
1302 * is transferred to a new value, or consumed if
1303 * we find a matching existing value.
1305 val_index
= ivace_reference_by_value(ivac
, new_value
);
1306 iv_set(voucher
, key_index
, val_index
);
1309 * release saved old value from the newly forming voucher
1310 * This is saved until the end to avoid churning the
1311 * release logic in cases where the same value is returned
1312 * as was there before.
1314 ivace_release(key_index
, save_val_index
);
1316 return KERN_SUCCESS
;
1319 static kern_return_t
1320 ipc_execute_voucher_recipe_command(
1321 ipc_voucher_t voucher
,
1322 mach_voucher_attr_key_t key
,
1323 mach_voucher_attr_recipe_command_t command
,
1324 ipc_voucher_t prev_iv
,
1325 mach_voucher_attr_content_t content
,
1326 mach_voucher_attr_content_size_t content_size
,
1329 iv_index_t prev_val_index
;
1330 iv_index_t val_index
;
1336 * MACH_VOUCHER_ATTR_COPY
1337 * Copy the attribute(s) from the previous voucher to the new
1338 * one. A wildcard key is an acceptable value - indicating a
1339 * desire to copy all the attribute values from the previous
1342 case MACH_VOUCHER_ATTR_COPY
:
1344 /* no recipe data on a copy */
1345 if (0 < content_size
)
1346 return KERN_INVALID_ARGUMENT
;
1348 /* nothing to copy from? - done */
1349 if (IV_NULL
== prev_iv
)
1350 return KERN_SUCCESS
;
1352 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1353 iv_index_t limit
, j
;
1355 /* reconcile possible difference in voucher sizes */
1356 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1357 prev_iv
->iv_table_size
:
1358 voucher
->iv_table_size
;
1360 /* wildcard matching */
1361 for (j
= 0; j
< limit
; j
++) {
1362 /* release old value being replaced */
1363 val_index
= iv_lookup(voucher
, j
);
1364 ivace_release(j
, val_index
);
1366 /* replace with reference to prev voucher's value */
1367 prev_val_index
= iv_lookup(prev_iv
, j
);
1368 ivace_reference_by_index(j
, prev_val_index
);
1369 iv_set(voucher
, j
, prev_val_index
);
1372 iv_index_t key_index
;
1374 /* copy just one key */
1375 key_index
= iv_key_to_index(key
);
1376 if (ivgt_keys_in_use
< key_index
)
1377 return KERN_INVALID_ARGUMENT
;
1379 /* release old value being replaced */
1380 val_index
= iv_lookup(voucher
, key_index
);
1381 ivace_release(key_index
, val_index
);
1383 /* replace with reference to prev voucher's value */
1384 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1385 ivace_reference_by_index(key_index
, prev_val_index
);
1386 iv_set(voucher
, key_index
, prev_val_index
);
1391 * MACH_VOUCHER_ATTR_REMOVE
1392 * Remove the attribute(s) from the under construction voucher.
1393 * A wildcard key is an acceptable value - indicating a desire
1394 * to remove all the attribute values set up so far in the voucher.
1395 * If a previous voucher is specified, only remove the value it
1396 * it matches the value in the previous voucher.
1398 case MACH_VOUCHER_ATTR_REMOVE
:
1399 /* no recipe data on a remove */
1400 if (0 < content_size
)
1401 return KERN_INVALID_ARGUMENT
;
1403 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1404 iv_index_t limit
, j
;
1406 /* reconcile possible difference in voucher sizes */
1407 limit
= (IV_NULL
== prev_iv
) ? voucher
->iv_table_size
:
1408 ((prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1409 prev_iv
->iv_table_size
: voucher
->iv_table_size
);
1411 /* wildcard matching */
1412 for (j
= 0; j
< limit
; j
++) {
1413 val_index
= iv_lookup(voucher
, j
);
1415 /* If not matched in previous, skip */
1416 if (IV_NULL
!= prev_iv
) {
1417 prev_val_index
= iv_lookup(prev_iv
, j
);
1418 if (val_index
!= prev_val_index
)
1421 /* release and clear */
1422 ivace_release(j
, val_index
);
1423 iv_set(voucher
, j
, IV_UNUSED_VALINDEX
);
1426 iv_index_t key_index
;
1428 /* copy just one key */
1429 key_index
= iv_key_to_index(key
);
1430 if (ivgt_keys_in_use
< key_index
)
1431 return KERN_INVALID_ARGUMENT
;
1433 val_index
= iv_lookup(voucher
, key_index
);
1435 /* If not matched in previous, skip */
1436 if (IV_NULL
!= prev_iv
) {
1437 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1438 if (val_index
!= prev_val_index
)
1442 /* release and clear */
1443 ivace_release(key_index
, val_index
);
1444 iv_set(voucher
, key_index
, IV_UNUSED_VALINDEX
);
1449 * MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
1450 * Use key-privilege to set a value handle for the attribute directly,
1451 * rather than triggering a callback into the attribute manager to
1452 * interpret a recipe to generate the value handle.
1454 case MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
:
1456 mach_voucher_attr_value_handle_t new_value
;
1458 if (sizeof(mach_voucher_attr_value_handle_t
) != content_size
)
1459 return KERN_INVALID_ARGUMENT
;
1461 new_value
= *(mach_voucher_attr_value_handle_t
*)(void *)content
;
1462 kr
= ipc_directly_replace_voucher_value(voucher
,
1465 if (KERN_SUCCESS
!= kr
)
1468 return KERN_INVALID_CAPABILITY
;
1472 * MACH_VOUCHER_ATTR_REDEEM
1473 * Redeem the attribute(s) from the previous voucher for a possibly
1474 * new value in the new voucher. A wildcard key is an acceptable value,
1475 * indicating a desire to redeem all the values.
1477 case MACH_VOUCHER_ATTR_REDEEM
:
1479 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1480 iv_index_t limit
, j
;
1482 /* reconcile possible difference in voucher sizes */
1483 if (IV_NULL
!= prev_iv
)
1484 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1485 prev_iv
->iv_table_size
:
1486 voucher
->iv_table_size
;
1488 limit
= voucher
->iv_table_size
;
1490 /* wildcard matching */
1491 for (j
= 0; j
< limit
; j
++) {
1492 mach_voucher_attr_key_t j_key
;
1494 j_key
= iv_index_to_key(j
);
1496 /* skip non-existent managers */
1497 if (MACH_VOUCHER_ATTR_KEY_NONE
== j_key
)
1500 /* get the new value from redeem (skip empty previous) */
1501 kr
= ipc_replace_voucher_value(voucher
,
1507 if (KERN_SUCCESS
!= kr
)
1512 /* fall thru for single key redemption */
1516 * Replace the current value for the <voucher, key> pair with whatever
1517 * value the resource manager returns for the command and recipe
1518 * combination provided.
1521 kr
= ipc_replace_voucher_value(voucher
,
1527 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 (0 < iv
->iv_refs
&& iv
->iv_sum
== sum
) {
1604 assert(iv
->iv_table_size
<= new_iv
->iv_table_size
);
1606 /* and common entries match... */
1607 for (i
= 0; i
< iv
->iv_table_size
; i
++)
1608 if (iv
->iv_table
[i
] != new_iv
->iv_table
[i
])
1610 if (i
< iv
->iv_table_size
)
1613 /* and all extra entries in new one are unused... */
1614 while (i
< new_iv
->iv_table_size
)
1615 if (new_iv
->iv_table
[i
++] != IV_UNUSED_VALINDEX
)
1617 if (i
< new_iv
->iv_table_size
)
1620 /* ... we found a match... */
1622 /* can we get a ref before it hits 0
1624 * This is thread safe. The reference is just an atomic
1625 * add. If the reference count is zero when we adjust it,
1626 * no other thread can have a reference to the voucher.
1627 * The dealloc code requires holding the ivht_lock, so
1628 * the voucher cannot be yanked out from under us.
1630 refs
= iv_reference(iv
);
1632 /* drats! going away. Put back to zero */
1639 /* referenced previous, so deallocate the new one */
1640 iv_dealloc(new_iv
, FALSE
);
1645 /* add the new voucher to the hash, and return it */
1646 new_iv
->iv_sum
= sum
;
1647 new_iv
->iv_hash
= hash
;
1648 queue_enter(&ivht_bucket
[hash
], new_iv
, ipc_voucher_t
, iv_hash_link
);
1653 * This code is disabled for KDEBUG_LEVEL_IST and KDEBUG_LEVEL_NONE
1655 #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD)
1656 if (kdebug_enable
& ~KDEBUG_ENABLE_PPT
) {
1657 uintptr_t voucher_addr
= VM_KERNEL_ADDRPERM((uintptr_t)new_iv
);
1658 uintptr_t attr_tracepoints_needed
= 0;
1660 if (ipc_voucher_trace_contents
) {
1662 * voucher_contents sizing is a bit more constrained
1663 * than might be obvious.
1665 * This is typically a uint8_t typed array. However,
1666 * we want to access it as a uintptr_t to efficiently
1667 * copyout the data in tracepoints.
1669 * This constrains the size to uintptr_t bytes, and
1670 * adds a minimimum alignment requirement equivalent
1673 * Further constraining the size is the fact that it
1674 * is copied out 4 uintptr_t chunks at a time. We do
1675 * NOT want to run off the end of the array and copyout
1676 * random stack data.
1678 * So the minimum size is 4 * sizeof(uintptr_t), and
1679 * the minimum alignment is uintptr_t aligned.
1682 #define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
1683 #define PAYLOAD_SIZE 1024
1685 _Static_assert(PAYLOAD_SIZE
% PAYLOAD_PER_TRACEPOINT
== 0, "size invariant violated");
1687 mach_voucher_attr_raw_recipe_array_size_t payload_size
= PAYLOAD_SIZE
;
1688 uintptr_t payload
[PAYLOAD_SIZE
/ sizeof(uintptr_t)];
1691 kr
= mach_voucher_extract_all_attr_recipes(new_iv
, (mach_voucher_attr_raw_recipe_array_t
)payload
, &payload_size
);
1692 if (KERN_SUCCESS
== kr
) {
1693 attr_tracepoints_needed
= (payload_size
+ PAYLOAD_PER_TRACEPOINT
- 1) / PAYLOAD_PER_TRACEPOINT
;
1696 * To prevent leaking data from the stack, we
1697 * need to zero data to the end of a tracepoint
1700 size_t remainder
= payload_size
% PAYLOAD_PER_TRACEPOINT
;
1702 bzero((uint8_t*)payload
+ payload_size
,
1703 PAYLOAD_PER_TRACEPOINT
- remainder
);
1707 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_CREATE
) | DBG_FUNC_NONE
,
1709 new_iv
->iv_table_size
, ivht_count
, payload_size
, 0);
1711 uintptr_t index
= 0;
1712 while (attr_tracepoints_needed
--) {
1713 KERNEL_DEBUG_CONSTANT1(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_CREATE_ATTR_DATA
) | DBG_FUNC_NONE
,
1722 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_CREATE
) | DBG_FUNC_NONE
,
1724 new_iv
->iv_table_size
, ivht_count
, 0, 0);
1727 #endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
1733 * Routine: ipc_create_mach_voucher
1735 * Create a new mach voucher and initialize it with the
1736 * value(s) created by having the appropriate resource
1737 * managers interpret the supplied recipe commands and
1740 * Nothing locked (may invoke user-space repeatedly).
1741 * Caller holds references on previous vouchers.
1742 * Previous vouchers are passed as voucher indexes.
1745 ipc_create_mach_voucher(
1746 ipc_voucher_attr_raw_recipe_array_t recipes
,
1747 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1748 ipc_voucher_t
*new_voucher
)
1750 ipc_voucher_attr_recipe_t sub_recipe
;
1751 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1752 ipc_voucher_t voucher
;
1753 kern_return_t kr
= KERN_SUCCESS
;
1755 /* if nothing to do ... */
1756 if (0 == recipe_size
) {
1757 *new_voucher
= IV_NULL
;
1758 return KERN_SUCCESS
;
1761 /* allocate a voucher */
1762 voucher
= iv_alloc(ivgt_keys_in_use
);
1763 if (IV_NULL
== voucher
)
1764 return KERN_RESOURCE_SHORTAGE
;
1766 /* iterate over the recipe items */
1767 while (0 < recipe_size
- recipe_used
) {
1769 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1770 kr
= KERN_INVALID_ARGUMENT
;
1774 /* find the next recipe */
1775 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1776 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1777 kr
= KERN_INVALID_ARGUMENT
;
1780 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1782 kr
= ipc_execute_voucher_recipe_command(voucher
,
1784 sub_recipe
->command
,
1785 sub_recipe
->previous_voucher
,
1786 sub_recipe
->content
,
1787 sub_recipe
->content_size
,
1789 if (KERN_SUCCESS
!= kr
)
1793 if (KERN_SUCCESS
== kr
) {
1794 *new_voucher
= iv_dedup(voucher
);
1796 iv_dealloc(voucher
, FALSE
);
1797 *new_voucher
= IV_NULL
;
1803 * Routine: ipc_voucher_attr_control_create_mach_voucher
1805 * Create a new mach voucher and initialize it with the
1806 * value(s) created by having the appropriate resource
1807 * managers interpret the supplied recipe commands and
1810 * The resource manager control's privilege over its
1811 * particular key value is reflected on to the execution
1812 * code, allowing internal commands (like setting a
1813 * key value handle directly, rather than having to
1814 * create a recipe, that will generate a callback just
1818 * Nothing locked (may invoke user-space repeatedly).
1819 * Caller holds references on previous vouchers.
1820 * Previous vouchers are passed as voucher indexes.
1823 ipc_voucher_attr_control_create_mach_voucher(
1824 ipc_voucher_attr_control_t control
,
1825 ipc_voucher_attr_raw_recipe_array_t recipes
,
1826 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1827 ipc_voucher_t
*new_voucher
)
1829 mach_voucher_attr_key_t control_key
;
1830 ipc_voucher_attr_recipe_t sub_recipe
;
1831 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1832 ipc_voucher_t voucher
= IV_NULL
;
1833 kern_return_t kr
= KERN_SUCCESS
;
1835 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
)
1836 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
;
1849 control_key
= iv_index_to_key(control
->ivac_key_index
);
1851 /* iterate over the recipe items */
1852 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
)
1878 if (KERN_SUCCESS
== kr
) {
1879 *new_voucher
= iv_dedup(voucher
);
1881 *new_voucher
= IV_NULL
;
1882 iv_dealloc(voucher
, FALSE
);
1888 * ipc_register_well_known_mach_voucher_attr_manager
1890 * Register the resource manager responsible for a given key value.
1893 ipc_register_well_known_mach_voucher_attr_manager(
1894 ipc_voucher_attr_manager_t manager
,
1895 mach_voucher_attr_value_handle_t default_value
,
1896 mach_voucher_attr_key_t key
,
1897 ipc_voucher_attr_control_t
*control
)
1899 ipc_voucher_attr_control_t new_control
;
1900 iv_index_t key_index
;
1901 iv_index_t hash_index
;
1903 if (IVAM_NULL
== manager
)
1904 return KERN_INVALID_ARGUMENT
;
1906 key_index
= iv_key_to_index(key
);
1907 if (IV_UNUSED_KEYINDEX
== key_index
)
1908 return KERN_INVALID_ARGUMENT
;
1910 new_control
= ivac_alloc(key_index
);
1911 if (IVAC_NULL
== new_control
)
1912 return KERN_RESOURCE_SHORTAGE
;
1914 /* insert the default value into slot 0 */
1915 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_value
= default_value
;
1916 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_refs
= IVACE_REFS_MAX
;
1917 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_made
= IVACE_REFS_MAX
;
1918 assert(IV_HASH_END
== new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_next
);
1921 if (IVAM_NULL
!= iv_global_table
[key_index
].ivgte_manager
) {
1923 ivac_release(new_control
);
1924 return KERN_INVALID_ARGUMENT
;
1927 /* fill in the global table slot for this key */
1928 iv_global_table
[key_index
].ivgte_manager
= manager
;
1929 iv_global_table
[key_index
].ivgte_control
= new_control
;
1930 iv_global_table
[key_index
].ivgte_key
= key
;
1932 /* insert the default value into the hash (in case it is returned later) */
1933 hash_index
= iv_hash_value(key_index
, default_value
);
1934 assert(IV_HASH_END
== new_control
->ivac_table
[hash_index
].ivace_index
);
1935 new_control
->ivac_table
[hash_index
].ivace_index
= IV_UNUSED_VALINDEX
;
1939 /* return the reference on the new cache control to the caller */
1940 *control
= new_control
;
1942 return KERN_SUCCESS
;
1946 * Routine: mach_voucher_extract_attr_content
1948 * Extract the content for a given <voucher, key> pair.
1950 * If a value other than the default is present for this
1951 * <voucher,key> pair, we need to contact the resource
1952 * manager to extract the content/meaning of the value(s)
1953 * present. Otherwise, return success (but no data).
1956 * Nothing locked - as it may upcall to user-space.
1957 * The caller holds a reference on the voucher.
1960 mach_voucher_extract_attr_content(
1961 ipc_voucher_t voucher
,
1962 mach_voucher_attr_key_t key
,
1963 mach_voucher_attr_content_t content
,
1964 mach_voucher_attr_content_size_t
*in_out_size
)
1966 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1967 mach_voucher_attr_value_handle_array_size_t vals_count
;
1968 mach_voucher_attr_recipe_command_t command
;
1969 ipc_voucher_attr_manager_t manager
;
1970 iv_index_t value_index
;
1971 iv_index_t key_index
;
1975 if (IV_NULL
== voucher
)
1976 return KERN_INVALID_ARGUMENT
;
1978 key_index
= iv_key_to_index(key
);
1980 value_index
= iv_lookup(voucher
, key_index
);
1981 if (IV_UNUSED_VALINDEX
== value_index
) {
1983 return KERN_SUCCESS
;
1987 * Get the manager for this key_index. The
1988 * existence of a non-default value for this
1989 * slot within our voucher will keep the
1990 * manager referenced during the callout.
1992 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
1993 assert(IVAM_NULL
!= manager
);
1996 * Get the value(s) to pass to the manager
1997 * for this value_index.
1999 ivace_lookup_values(key_index
, value_index
,
2001 assert(0 < vals_count
);
2003 /* callout to manager */
2005 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2008 content
, in_out_size
);
2013 * Routine: mach_voucher_extract_attr_recipe
2015 * Extract a recipe for a given <voucher, key> pair.
2017 * If a value other than the default is present for this
2018 * <voucher,key> pair, we need to contact the resource
2019 * manager to extract the content/meaning of the value(s)
2020 * present. Otherwise, return success (but no data).
2023 * Nothing locked - as it may upcall to user-space.
2024 * The caller holds a reference on the voucher.
2027 mach_voucher_extract_attr_recipe(
2028 ipc_voucher_t voucher
,
2029 mach_voucher_attr_key_t key
,
2030 mach_voucher_attr_raw_recipe_t raw_recipe
,
2031 mach_voucher_attr_raw_recipe_size_t
*in_out_size
)
2033 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2034 mach_voucher_attr_value_handle_array_size_t vals_count
;
2035 ipc_voucher_attr_manager_t manager
;
2036 mach_voucher_attr_recipe_t recipe
;
2037 iv_index_t value_index
;
2038 iv_index_t key_index
;
2042 if (IV_NULL
== voucher
)
2043 return KERN_INVALID_ARGUMENT
;
2045 key_index
= iv_key_to_index(key
);
2047 value_index
= iv_lookup(voucher
, key_index
);
2048 if (IV_UNUSED_VALINDEX
== value_index
) {
2050 return KERN_SUCCESS
;
2053 if (*in_out_size
< sizeof(*recipe
))
2054 return KERN_NO_SPACE
;
2056 recipe
= (mach_voucher_attr_recipe_t
)(void *)raw_recipe
;
2058 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2059 recipe
->previous_voucher
= MACH_VOUCHER_NAME_NULL
;
2060 recipe
->content_size
= *in_out_size
- sizeof(*recipe
);
2063 * Get the manager for this key_index. The
2064 * existence of a non-default value for this
2065 * slot within our voucher will keep the
2066 * manager referenced during the callout.
2068 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2069 assert(IVAM_NULL
!= manager
);
2072 * Get the value(s) to pass to the manager
2073 * for this value_index.
2075 ivace_lookup_values(key_index
, value_index
,
2077 assert(0 < vals_count
);
2079 /* callout to manager */
2080 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2083 recipe
->content
, &recipe
->content_size
);
2084 if (KERN_SUCCESS
== kr
) {
2085 assert(*in_out_size
- sizeof(*recipe
) >= recipe
->content_size
);
2086 *in_out_size
= sizeof(*recipe
) + recipe
->content_size
;
2095 * Routine: mach_voucher_extract_all_attr_recipes
2097 * Extract all the (non-default) contents for a given voucher,
2098 * building up a recipe that could be provided to a future
2099 * voucher creation call.
2101 * Nothing locked (may invoke user-space).
2102 * Caller holds a reference on the supplied voucher.
2105 mach_voucher_extract_all_attr_recipes(
2106 ipc_voucher_t voucher
,
2107 mach_voucher_attr_raw_recipe_array_t recipes
,
2108 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2110 mach_voucher_attr_recipe_size_t recipe_size
= *in_out_size
;
2111 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2112 iv_index_t key_index
;
2114 if (IV_NULL
== voucher
)
2115 return KERN_INVALID_ARGUMENT
;
2117 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2118 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2119 mach_voucher_attr_value_handle_array_size_t vals_count
;
2120 mach_voucher_attr_content_size_t content_size
;
2121 ipc_voucher_attr_manager_t manager
;
2122 mach_voucher_attr_recipe_t recipe
;
2123 mach_voucher_attr_key_t key
;
2124 iv_index_t value_index
;
2127 /* don't output anything for a default value */
2128 value_index
= iv_lookup(voucher
, key_index
);
2129 if (IV_UNUSED_VALINDEX
== value_index
)
2132 if (recipe_size
- recipe_used
< sizeof(*recipe
))
2133 return KERN_NO_SPACE
;
2135 recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2136 content_size
= recipe_size
- recipe_used
- sizeof(*recipe
);
2139 * Get the manager for this key_index. The
2140 * existence of a non-default value for this
2141 * slot within our voucher will keep the
2142 * manager referenced during the callout.
2144 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2145 assert(IVAM_NULL
!= manager
);
2148 * Get the value(s) to pass to the manager
2149 * for this value_index.
2151 ivace_lookup_values(key_index
, value_index
,
2153 assert(0 < vals_count
);
2155 key
= iv_index_to_key(key_index
);
2158 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2159 recipe
->content_size
= content_size
;
2161 /* callout to manager */
2162 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2165 recipe
->content
, &recipe
->content_size
);
2166 if (KERN_SUCCESS
!= kr
)
2169 assert(recipe
->content_size
<= content_size
);
2170 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2173 *in_out_size
= recipe_used
;
2174 return KERN_SUCCESS
;
2178 * Routine: mach_voucher_debug_info
2180 * Extract all the (non-default) contents for a given mach port name,
2181 * building up a recipe that could be provided to a future
2182 * voucher creation call.
2184 * Nothing locked (may invoke user-space).
2185 * Caller may not hold a reference on the supplied voucher.
2187 #if !(DEVELOPMENT || DEBUG)
2189 mach_voucher_debug_info(
2190 ipc_space_t __unused space
,
2191 mach_port_name_t __unused voucher_name
,
2192 mach_voucher_attr_raw_recipe_array_t __unused recipes
,
2193 mach_voucher_attr_raw_recipe_array_size_t __unused
*in_out_size
)
2195 return KERN_NOT_SUPPORTED
;
2199 mach_voucher_debug_info(
2201 mach_port_name_t voucher_name
,
2202 mach_voucher_attr_raw_recipe_array_t recipes
,
2203 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2205 ipc_voucher_t voucher
= IPC_VOUCHER_NULL
;
2207 ipc_port_t port
= MACH_PORT_NULL
;
2209 if (!MACH_PORT_VALID(voucher_name
)) {
2210 return KERN_INVALID_ARGUMENT
;
2213 kr
= ipc_port_translate_send(space
, voucher_name
, &port
);
2214 if (KERN_SUCCESS
!= kr
)
2215 return KERN_INVALID_ARGUMENT
;
2217 voucher
= convert_port_to_voucher(port
);
2221 kr
= mach_voucher_extract_all_attr_recipes(voucher
, recipes
, in_out_size
);
2222 ipc_voucher_release(voucher
);
2226 return KERN_FAILURE
;
2231 * Routine: mach_voucher_attr_command
2233 * Invoke an attribute-specific command through this voucher.
2235 * The voucher layout, membership, etc... is not altered
2236 * through the execution of this command.
2239 * Nothing locked - as it may upcall to user-space.
2240 * The caller holds a reference on the voucher.
2243 mach_voucher_attr_command(
2244 ipc_voucher_t voucher
,
2245 mach_voucher_attr_key_t key
,
2246 mach_voucher_attr_command_t command
,
2247 mach_voucher_attr_content_t in_content
,
2248 mach_voucher_attr_content_size_t in_content_size
,
2249 mach_voucher_attr_content_t out_content
,
2250 mach_voucher_attr_content_size_t
*out_content_size
)
2252 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2253 mach_voucher_attr_value_handle_array_size_t vals_count
;
2254 ipc_voucher_attr_manager_t manager
;
2255 ipc_voucher_attr_control_t control
;
2256 iv_index_t value_index
;
2257 iv_index_t key_index
;
2261 if (IV_NULL
== voucher
)
2262 return KERN_INVALID_ARGUMENT
;
2264 key_index
= iv_key_to_index(key
);
2267 * Get the manager for this key_index.
2268 * Allowing commands against the default value
2269 * for an attribute means that we have to hold
2270 * reference on the attribute manager control
2271 * to keep the manager around during the command
2274 ivgt_lookup(key_index
, TRUE
, &manager
, &control
);
2275 assert(IVAM_NULL
!= manager
);
2278 * Get the values for this <voucher, key> pair
2279 * to pass to the attribute manager. It is still
2280 * permissible to execute a command against the
2281 * default value (empty value array).
2283 value_index
= iv_lookup(voucher
, key_index
);
2284 ivace_lookup_values(key_index
, value_index
,
2287 /* callout to manager */
2288 kr
= (manager
->ivam_command
)(manager
, key
,
2291 in_content
, in_content_size
,
2292 out_content
, out_content_size
);
2294 /* release reference on control */
2295 ivac_release(control
);
2301 * Routine: mach_voucher_attr_control_get_values
2303 * For a given voucher, get the value handle associated with the
2304 * specified attribute manager.
2307 mach_voucher_attr_control_get_values(
2308 ipc_voucher_attr_control_t control
,
2309 ipc_voucher_t voucher
,
2310 mach_voucher_attr_value_handle_array_t out_values
,
2311 mach_voucher_attr_value_handle_array_size_t
*in_out_size
)
2313 iv_index_t key_index
, value_index
;
2315 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
)
2316 return KERN_INVALID_CAPABILITY
;
2318 if (IV_NULL
== voucher
)
2319 return KERN_INVALID_ARGUMENT
;
2321 if (0 == *in_out_size
)
2322 return KERN_SUCCESS
;
2324 key_index
= control
->ivac_key_index
;
2326 assert(0 < voucher
->iv_refs
);
2327 value_index
= iv_lookup(voucher
, key_index
);
2328 ivace_lookup_values(key_index
, value_index
,
2329 out_values
, in_out_size
);
2330 return KERN_SUCCESS
;
2335 * Routine: mach_voucher_attr_control_create_mach_voucher
2337 * Create a new mach voucher and initialize it by processing the
2338 * supplied recipe(s).
2340 * Coming in on the attribute control port denotes special privileges
2341 * over they key associated with the control port.
2343 * Coming in from user-space, each recipe item will have a previous
2344 * recipe port name that needs to be converted to a voucher. Because
2345 * we can't rely on the port namespace to hold a reference on each
2346 * previous voucher port for the duration of processing that command,
2347 * we have to convert the name to a voucher reference and release it
2348 * after the command processing is done.
2351 mach_voucher_attr_control_create_mach_voucher(
2352 ipc_voucher_attr_control_t control
,
2353 mach_voucher_attr_raw_recipe_array_t recipes
,
2354 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2355 ipc_voucher_t
*new_voucher
)
2357 mach_voucher_attr_key_t control_key
;
2358 mach_voucher_attr_recipe_t sub_recipe
;
2359 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2360 ipc_voucher_t voucher
= IV_NULL
;
2361 kern_return_t kr
= KERN_SUCCESS
;
2363 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
)
2364 return KERN_INVALID_CAPABILITY
;
2366 /* if nothing to do ... */
2367 if (0 == recipe_size
) {
2368 *new_voucher
= IV_NULL
;
2369 return KERN_SUCCESS
;
2372 /* allocate new voucher */
2373 voucher
= iv_alloc(ivgt_keys_in_use
);
2374 if (IV_NULL
== voucher
)
2375 return KERN_RESOURCE_SHORTAGE
;
2377 control_key
= iv_index_to_key(control
->ivac_key_index
);
2379 /* iterate over the recipe items */
2380 while (0 < recipe_size
- recipe_used
) {
2381 ipc_voucher_t prev_iv
;
2383 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2384 kr
= KERN_INVALID_ARGUMENT
;
2388 /* find the next recipe */
2389 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2390 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2391 kr
= KERN_INVALID_ARGUMENT
;
2394 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2396 /* convert voucher port name (current space) into a voucher reference */
2397 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2398 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2399 kr
= KERN_INVALID_CAPABILITY
;
2403 kr
= ipc_execute_voucher_recipe_command(voucher
,
2405 sub_recipe
->command
,
2407 sub_recipe
->content
,
2408 sub_recipe
->content_size
,
2409 (sub_recipe
->key
== control_key
));
2410 ipc_voucher_release(prev_iv
);
2412 if (KERN_SUCCESS
!= kr
)
2416 if (KERN_SUCCESS
== kr
) {
2417 *new_voucher
= iv_dedup(voucher
);
2419 *new_voucher
= IV_NULL
;
2420 iv_dealloc(voucher
, FALSE
);
2426 * Routine: host_create_mach_voucher
2428 * Create a new mach voucher and initialize it by processing the
2429 * supplied recipe(s).
2431 * Comming in from user-space, each recipe item will have a previous
2432 * recipe port name that needs to be converted to a voucher. Because
2433 * we can't rely on the port namespace to hold a reference on each
2434 * previous voucher port for the duration of processing that command,
2435 * we have to convert the name to a voucher reference and release it
2436 * after the command processing is done.
2439 host_create_mach_voucher(
2441 mach_voucher_attr_raw_recipe_array_t recipes
,
2442 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2443 ipc_voucher_t
*new_voucher
)
2445 mach_voucher_attr_recipe_t sub_recipe
;
2446 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2447 ipc_voucher_t voucher
= IV_NULL
;
2448 kern_return_t kr
= KERN_SUCCESS
;
2450 if (host
== HOST_NULL
)
2451 return KERN_INVALID_ARGUMENT
;
2453 /* if nothing to do ... */
2454 if (0 == recipe_size
) {
2455 *new_voucher
= IV_NULL
;
2456 return KERN_SUCCESS
;
2459 /* allocate new voucher */
2460 voucher
= iv_alloc(ivgt_keys_in_use
);
2461 if (IV_NULL
== voucher
)
2462 return KERN_RESOURCE_SHORTAGE
;
2464 /* iterate over the recipe items */
2465 while (0 < recipe_size
- recipe_used
) {
2466 ipc_voucher_t prev_iv
;
2468 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2469 kr
= KERN_INVALID_ARGUMENT
;
2473 /* find the next recipe */
2474 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2475 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2476 kr
= KERN_INVALID_ARGUMENT
;
2479 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2481 /* convert voucher port name (current space) into a voucher reference */
2482 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2483 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2484 kr
= KERN_INVALID_CAPABILITY
;
2488 kr
= ipc_execute_voucher_recipe_command(voucher
,
2490 sub_recipe
->command
,
2492 sub_recipe
->content
,
2493 sub_recipe
->content_size
,
2495 ipc_voucher_release(prev_iv
);
2497 if (KERN_SUCCESS
!= kr
)
2501 if (KERN_SUCCESS
== kr
) {
2502 *new_voucher
= iv_dedup(voucher
);
2504 *new_voucher
= IV_NULL
;
2505 iv_dealloc(voucher
, FALSE
);
2511 * Routine: host_register_well_known_mach_voucher_attr_manager
2513 * Register the user-level resource manager responsible for a given
2516 * The manager port passed in has to be converted/wrapped
2517 * in an ipc_voucher_attr_manager_t structure and then call the
2518 * internal variant. We have a generic ipc voucher manager
2519 * type that implements a MIG proxy out to user-space just for
2523 host_register_well_known_mach_voucher_attr_manager(
2525 mach_voucher_attr_manager_t __unused manager
,
2526 mach_voucher_attr_value_handle_t __unused default_value
,
2527 mach_voucher_attr_key_t __unused key
,
2528 ipc_voucher_attr_control_t __unused
*control
)
2530 if (HOST_NULL
== host
)
2531 return KERN_INVALID_HOST
;
2534 return KERN_NOT_SUPPORTED
;
2537 * Allocate a mig_voucher_attr_manager_t that provides the
2538 * MIG proxy functions for the three manager callbacks and
2539 * store the port right in there.
2541 * If the user-space manager dies, we'll detect it on our
2542 * next upcall, and cleanup the proxy at that point.
2544 mig_voucher_attr_manager_t proxy
;
2547 proxy
= mvam_alloc(manager
);
2549 kr
= ipc_register_well_known_mach_voucher_attr_manager(&proxy
->mvam_manager
,
2553 if (KERN_SUCCESS
!= kr
)
2554 mvam_release(proxy
);
2561 * Routine: host_register_mach_voucher_attr_manager
2563 * Register the user-space resource manager and return a
2564 * dynamically allocated key.
2566 * Wrap the supplied port with the MIG proxy ipc
2567 * voucher resource manager, and then call the internal
2571 host_register_mach_voucher_attr_manager(
2573 mach_voucher_attr_manager_t __unused manager
,
2574 mach_voucher_attr_value_handle_t __unused default_value
,
2575 mach_voucher_attr_key_t __unused
*key
,
2576 ipc_voucher_attr_control_t __unused
*control
)
2578 if (HOST_NULL
== host
)
2579 return KERN_INVALID_HOST
;
2581 return KERN_NOT_SUPPORTED
;
2585 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2588 * Build-in a simple User Data Resource Manager
2590 #define USER_DATA_MAX_DATA (16*1024)
2592 struct user_data_value_element
{
2593 mach_voucher_attr_value_reference_t e_made
;
2594 mach_voucher_attr_content_size_t e_size
;
2597 queue_chain_t e_hash_link
;
2601 typedef struct user_data_value_element
*user_data_element_t
;
2604 * User Data Voucher Hash Table
2606 #define USER_DATA_HASH_BUCKETS 127
2607 #define USER_DATA_HASH_BUCKET(x) ((x) % USER_DATA_HASH_BUCKETS)
2609 static queue_head_t user_data_bucket
[USER_DATA_HASH_BUCKETS
];
2610 static lck_spin_t user_data_lock_data
;
2612 #define user_data_lock_init() \
2613 lck_spin_init(&user_data_lock_data, &ipc_lck_grp, &ipc_lck_attr)
2614 #define user_data_lock_destroy() \
2615 lck_spin_destroy(&user_data_lock_data, &ipc_lck_grp)
2616 #define user_data_lock() \
2617 lck_spin_lock(&user_data_lock_data)
2618 #define user_data_lock_try() \
2619 lck_spin_try_lock(&user_data_lock_data)
2620 #define user_data_unlock() \
2621 lck_spin_unlock(&user_data_lock_data)
2623 static kern_return_t
2624 user_data_release_value(
2625 ipc_voucher_attr_manager_t manager
,
2626 mach_voucher_attr_key_t key
,
2627 mach_voucher_attr_value_handle_t value
,
2628 mach_voucher_attr_value_reference_t sync
);
2630 static kern_return_t
2631 user_data_get_value(
2632 ipc_voucher_attr_manager_t manager
,
2633 mach_voucher_attr_key_t key
,
2634 mach_voucher_attr_recipe_command_t command
,
2635 mach_voucher_attr_value_handle_array_t prev_values
,
2636 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
2637 mach_voucher_attr_content_t content
,
2638 mach_voucher_attr_content_size_t content_size
,
2639 mach_voucher_attr_value_handle_t
*out_value
,
2640 ipc_voucher_t
*out_value_voucher
);
2642 static kern_return_t
2643 user_data_extract_content(
2644 ipc_voucher_attr_manager_t manager
,
2645 mach_voucher_attr_key_t key
,
2646 mach_voucher_attr_value_handle_array_t values
,
2647 mach_voucher_attr_value_handle_array_size_t value_count
,
2648 mach_voucher_attr_recipe_command_t
*out_command
,
2649 mach_voucher_attr_content_t out_content
,
2650 mach_voucher_attr_content_size_t
*in_out_content_size
);
2652 static kern_return_t
2654 ipc_voucher_attr_manager_t manager
,
2655 mach_voucher_attr_key_t key
,
2656 mach_voucher_attr_value_handle_array_t values
,
2657 mach_msg_type_number_t value_count
,
2658 mach_voucher_attr_command_t command
,
2659 mach_voucher_attr_content_t in_content
,
2660 mach_voucher_attr_content_size_t in_content_size
,
2661 mach_voucher_attr_content_t out_content
,
2662 mach_voucher_attr_content_size_t
*out_content_size
);
2666 ipc_voucher_attr_manager_t manager
);
2668 struct ipc_voucher_attr_manager user_data_manager
= {
2669 .ivam_release_value
= user_data_release_value
,
2670 .ivam_get_value
= user_data_get_value
,
2671 .ivam_extract_content
= user_data_extract_content
,
2672 .ivam_command
= user_data_command
,
2673 .ivam_release
= user_data_release
,
2676 ipc_voucher_attr_control_t user_data_control
;
2677 ipc_voucher_attr_control_t test_control
;
2679 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) && defined(MACH_VOUCHER_ATTR_KEY_TEST)
2680 #define USER_DATA_ASSERT_KEY(key) \
2681 assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key) || \
2682 MACH_VOUCHER_ATTR_KEY_TEST == (key));
2683 #elif defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
2684 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key))
2686 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_TEST == (key))
2690 * Routine: user_data_release_value
2692 * Release a made reference on a specific value managed by
2693 * this voucher attribute manager.
2695 * Must remove the element associated with this value from
2696 * the hash if this is the last know made reference.
2698 static kern_return_t
2699 user_data_release_value(
2700 ipc_voucher_attr_manager_t __assert_only manager
,
2701 mach_voucher_attr_key_t __assert_only key
,
2702 mach_voucher_attr_value_handle_t value
,
2703 mach_voucher_attr_value_reference_t sync
)
2705 user_data_element_t elem
;
2708 assert (&user_data_manager
== manager
);
2709 USER_DATA_ASSERT_KEY(key
);
2711 elem
= (user_data_element_t
)value
;
2712 hash
= elem
->e_hash
;
2715 if (sync
== elem
->e_made
) {
2716 queue_remove(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
);
2718 kfree(elem
, sizeof(*elem
) + elem
->e_size
);
2719 return KERN_SUCCESS
;
2721 assert(sync
< elem
->e_made
);
2724 return KERN_FAILURE
;
2728 * Routine: user_data_checksum
2730 * Provide a rudimentary checksum for the data presented
2731 * to these voucher attribute managers.
2735 mach_voucher_attr_content_t content
,
2736 mach_voucher_attr_content_size_t content_size
)
2738 mach_voucher_attr_content_size_t i
;
2739 iv_index_t cksum
= 0;
2741 for(i
= 0; i
< content_size
; i
++, content
++) {
2742 cksum
= (cksum
<< 8) ^ (cksum
+ *(unsigned char *)content
);
2749 * Routine: user_data_dedup
2751 * See if the content represented by this request already exists
2752 * in another user data element. If so return a made reference
2753 * to the existing element. Otherwise, create a new element and
2754 * return that (after inserting it in the hash).
2758 * A made reference on the user_data_element_t
2760 static user_data_element_t
2762 mach_voucher_attr_content_t content
,
2763 mach_voucher_attr_content_size_t content_size
)
2767 user_data_element_t elem
;
2768 user_data_element_t alloc
= NULL
;
2770 sum
= user_data_checksum(content
, content_size
);
2771 hash
= USER_DATA_HASH_BUCKET(sum
);
2775 queue_iterate(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
) {
2776 assert(elem
->e_hash
== hash
);
2778 /* if sums match... */
2779 if (elem
->e_sum
== sum
&& elem
->e_size
== content_size
) {
2782 /* and all data matches */
2783 for (i
= 0; i
< content_size
; i
++)
2784 if (elem
->e_data
[i
] != content
[i
])
2786 if (i
< content_size
)
2789 /* ... we found a match... */
2795 kfree(alloc
, sizeof(*alloc
) + content_size
);
2801 if (NULL
== alloc
) {
2804 alloc
= (user_data_element_t
)kalloc(sizeof(*alloc
) + content_size
);
2806 alloc
->e_size
= content_size
;
2808 alloc
->e_hash
= hash
;
2809 memcpy(alloc
->e_data
, content
, content_size
);
2813 queue_enter(&user_data_bucket
[hash
], alloc
, user_data_element_t
, e_hash_link
);
2819 static kern_return_t
2820 user_data_get_value(
2821 ipc_voucher_attr_manager_t __assert_only manager
,
2822 mach_voucher_attr_key_t __assert_only key
,
2823 mach_voucher_attr_recipe_command_t command
,
2824 mach_voucher_attr_value_handle_array_t prev_values
,
2825 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
2826 mach_voucher_attr_content_t content
,
2827 mach_voucher_attr_content_size_t content_size
,
2828 mach_voucher_attr_value_handle_t
*out_value
,
2829 ipc_voucher_t
*out_value_voucher
)
2831 user_data_element_t elem
;
2833 assert (&user_data_manager
== manager
);
2834 USER_DATA_ASSERT_KEY(key
);
2836 /* never an out voucher */
2837 *out_value_voucher
= IPC_VOUCHER_NULL
;
2841 case MACH_VOUCHER_ATTR_REDEEM
:
2843 /* redeem of previous values is the value */
2844 if (0 < prev_value_count
) {
2845 elem
= (user_data_element_t
)prev_values
[0];
2846 assert(0 < elem
->e_made
);
2848 *out_value
= prev_values
[0];
2849 return KERN_SUCCESS
;
2852 /* redeem of default is default */
2854 return KERN_SUCCESS
;
2856 case MACH_VOUCHER_ATTR_USER_DATA_STORE
:
2857 if (USER_DATA_MAX_DATA
< content_size
)
2858 return KERN_RESOURCE_SHORTAGE
;
2860 /* empty is the default */
2861 if (0 == content_size
) {
2863 return KERN_SUCCESS
;
2866 elem
= user_data_dedup(content
, content_size
);
2867 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
2868 return KERN_SUCCESS
;
2871 /* every other command is unknown */
2872 return KERN_INVALID_ARGUMENT
;
2876 static kern_return_t
2877 user_data_extract_content(
2878 ipc_voucher_attr_manager_t __assert_only manager
,
2879 mach_voucher_attr_key_t __assert_only key
,
2880 mach_voucher_attr_value_handle_array_t values
,
2881 mach_voucher_attr_value_handle_array_size_t value_count
,
2882 mach_voucher_attr_recipe_command_t
*out_command
,
2883 mach_voucher_attr_content_t out_content
,
2884 mach_voucher_attr_content_size_t
*in_out_content_size
)
2886 mach_voucher_attr_content_size_t size
= 0;
2887 user_data_element_t elem
;
2890 assert (&user_data_manager
== manager
);
2891 USER_DATA_ASSERT_KEY(key
);
2893 /* concatenate the stored data items */
2894 for (i
= 0; i
< value_count
; i
++) {
2895 elem
= (user_data_element_t
)values
[i
];
2896 assert(USER_DATA_MAX_DATA
>= elem
->e_size
);
2898 if (size
+ elem
->e_size
> *in_out_content_size
)
2899 return KERN_NO_SPACE
;
2901 memcpy(&out_content
[size
], elem
->e_data
, elem
->e_size
);
2902 size
+= elem
->e_size
;
2904 *out_command
= MACH_VOUCHER_ATTR_BITS_STORE
;
2905 *in_out_content_size
= size
;
2906 return KERN_SUCCESS
;
2909 static kern_return_t
2911 ipc_voucher_attr_manager_t __assert_only manager
,
2912 mach_voucher_attr_key_t __assert_only key
,
2913 mach_voucher_attr_value_handle_array_t __unused values
,
2914 mach_msg_type_number_t __unused value_count
,
2915 mach_voucher_attr_command_t __unused command
,
2916 mach_voucher_attr_content_t __unused in_content
,
2917 mach_voucher_attr_content_size_t __unused in_content_size
,
2918 mach_voucher_attr_content_t __unused out_content
,
2919 mach_voucher_attr_content_size_t __unused
*out_content_size
)
2921 assert (&user_data_manager
== manager
);
2922 USER_DATA_ASSERT_KEY(key
);
2923 return KERN_FAILURE
;
2928 ipc_voucher_attr_manager_t manager
)
2930 if (manager
!= &user_data_manager
)
2933 panic("Voucher user-data manager released");
2936 static int user_data_manager_inited
= 0;
2939 user_data_attr_manager_init()
2943 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
2944 if ((user_data_manager_inited
& 0x1) != 0x1) {
2945 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
2946 (mach_voucher_attr_value_handle_t
)0,
2947 MACH_VOUCHER_ATTR_KEY_USER_DATA
,
2948 &user_data_control
);
2949 if (KERN_SUCCESS
!= kr
)
2950 printf("Voucher user-data manager register(USER-DATA) returned %d", kr
);
2952 user_data_manager_inited
|= 0x1;
2955 #if defined(MACH_VOUCHER_ATTR_KEY_TEST)
2956 if ((user_data_manager_inited
& 0x2) != 0x2) {
2957 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
2958 (mach_voucher_attr_value_handle_t
)0,
2959 MACH_VOUCHER_ATTR_KEY_TEST
,
2961 if (KERN_SUCCESS
!= kr
)
2962 printf("Voucher user-data manager register(TEST) returned %d", kr
);
2964 user_data_manager_inited
|= 0x2;
2967 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2970 for (i
=0; i
< USER_DATA_HASH_BUCKETS
; i
++)
2971 queue_init(&user_data_bucket
[i
]);
2973 user_data_lock_init();
2977 #endif /* MACH_DEBUG */