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 ipc_voucher_prepare_processing_recipe(
188 ipc_voucher_t voucher
,
189 ipc_voucher_attr_raw_recipe_array_t recipes
,
190 ipc_voucher_attr_raw_recipe_array_size_t
*in_out_size
,
191 mach_voucher_attr_recipe_command_t command
,
192 ipc_voucher_attr_manager_flags flags
,
193 int *need_processing
);
195 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
196 void user_data_attr_manager_init(void);
200 ipc_voucher_init(void)
202 natural_t ipc_voucher_max
= (task_max
+ thread_max
) * 2;
203 natural_t attr_manager_max
= MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
;
206 ipc_voucher_zone
= zinit(sizeof(struct ipc_voucher
),
207 ipc_voucher_max
* sizeof(struct ipc_voucher
),
208 sizeof(struct ipc_voucher
),
210 zone_change(ipc_voucher_zone
, Z_NOENCRYPT
, TRUE
);
212 ipc_voucher_attr_control_zone
= zinit(sizeof(struct ipc_voucher_attr_control
),
213 attr_manager_max
* sizeof(struct ipc_voucher_attr_control
),
214 sizeof(struct ipc_voucher_attr_control
),
215 "ipc voucher attr controls");
216 zone_change(ipc_voucher_attr_control_zone
, Z_NOENCRYPT
, TRUE
);
218 /* initialize voucher hash */
220 for (i
= 0; i
< IV_HASH_BUCKETS
; i
++)
221 queue_init(&ivht_bucket
[i
]);
223 /* initialize global table locking */
226 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
227 user_data_attr_manager_init();
232 iv_alloc(iv_index_t entries
)
238 iv
= (ipc_voucher_t
)zalloc(ipc_voucher_zone
);
245 iv
->iv_port
= IP_NULL
;
247 if (entries
> IV_ENTRIES_INLINE
) {
250 /* TODO - switch to ipc_table method of allocation */
251 table
= (iv_entry_t
) kalloc(sizeof(*table
) * entries
);
252 if (IVE_NULL
== table
) {
253 zfree(ipc_voucher_zone
, iv
);
256 iv
->iv_table
= table
;
257 iv
->iv_table_size
= entries
;
259 iv
->iv_table
= iv
->iv_inline_table
;
260 iv
->iv_table_size
= IV_ENTRIES_INLINE
;
263 /* initialize the table entries */
264 for (i
=0; i
< iv
->iv_table_size
; i
++)
265 iv
->iv_table
[i
] = IV_UNUSED_VALINDEX
;
273 * Set the voucher's value index for a given key index.
275 * This is only called during voucher creation, as
276 * they are immutable once references are distributed.
279 iv_set(ipc_voucher_t iv
,
280 iv_index_t key_index
,
281 iv_index_t value_index
)
283 assert(key_index
< iv
->iv_table_size
);
284 iv
->iv_table
[key_index
] = value_index
;
288 iv_dealloc(ipc_voucher_t iv
, boolean_t unhash
)
290 ipc_port_t port
= iv
->iv_port
;
294 * Do we have to remove it from the hash?
298 assert(0 == iv
->iv_refs
);
299 assert(IV_HASH_BUCKETS
> iv
->iv_hash
);
300 queue_remove(&ivht_bucket
[iv
->iv_hash
], iv
, ipc_voucher_t
, iv_hash_link
);
304 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_DESTROY
) | DBG_FUNC_NONE
,
305 VM_KERNEL_ADDRPERM((uintptr_t)iv
), 0, ivht_count
, 0, 0);
308 assert(0 == --iv
->iv_refs
);
311 * if a port was allocated for this voucher,
312 * it must not have any remaining send rights,
313 * because the port's reference on the voucher
314 * is gone. We can just discard it now.
316 if (IP_VALID(port
)) {
317 assert(ip_active(port
));
318 assert(port
->ip_srights
== 0);
320 ipc_port_dealloc_kernel(port
);
323 /* release the attribute references held by this voucher */
324 for (i
= 0; i
< iv
->iv_table_size
; i
++) {
325 ivace_release(i
, iv
->iv_table
[i
]);
331 if (iv
->iv_table
!= iv
->iv_inline_table
)
333 iv
->iv_table_size
* sizeof(*iv
->iv_table
));
335 zfree(ipc_voucher_zone
, iv
);
341 * Find the voucher's value index for a given key_index
343 * Vouchers are immutable, so no locking required to do
346 static inline iv_index_t
347 iv_lookup(ipc_voucher_t iv
, iv_index_t key_index
)
349 if (key_index
< iv
->iv_table_size
)
350 return iv
->iv_table
[key_index
];
351 return IV_UNUSED_VALINDEX
;
355 * Routine: unsafe_convert_port_to_voucher
357 * Unsafe conversion of a port to a voucher.
358 * Intended only for use by trace and debugging
359 * code. Consumes nothing, validates very little,
360 * produces an unreferenced voucher, which you
361 * MAY NOT use as a voucher, only log as an
364 * Caller has a send-right reference to port.
365 * Port may or may not be locked.
368 unsafe_convert_port_to_voucher(
371 if (IP_VALID(port
)) {
372 uintptr_t voucher
= (uintptr_t) port
->ip_kobject
;
375 * No need to lock because we have a reference on the
376 * port, and if it is a true voucher port, that reference
377 * keeps the voucher bound to the port (and active).
379 if (ip_kotype(port
) == IKOT_VOUCHER
)
382 return (uintptr_t)IV_NULL
;
386 * Routine: convert_port_to_voucher
388 * Convert from a port to a voucher.
389 * Doesn't consume the port [send-right] ref;
390 * produces a voucher ref, which may be null.
392 * Caller has a send-right reference to port.
393 * Port may or may not be locked.
396 convert_port_to_voucher(
399 if (IP_VALID(port
)) {
400 ipc_voucher_t voucher
= (ipc_voucher_t
) port
->ip_kobject
;
403 * No need to lock because we have a reference on the
404 * port, and if it is a true voucher port, that reference
405 * keeps the voucher bound to the port (and active).
407 if (ip_kotype(port
) != IKOT_VOUCHER
)
410 assert(ip_active(port
));
412 ipc_voucher_reference(voucher
);
419 * Routine: convert_port_name_to_voucher
421 * Convert from a port name in the current space to a voucher.
422 * Produces a voucher ref, which may be null.
428 convert_port_name_to_voucher(
429 mach_port_name_t voucher_name
)
435 if (MACH_PORT_VALID(voucher_name
)) {
436 kr
= ipc_port_translate_send(current_space(), voucher_name
, &port
);
437 if (KERN_SUCCESS
!= kr
)
440 iv
= convert_port_to_voucher(port
);
449 ipc_voucher_reference(ipc_voucher_t voucher
)
453 if (IPC_VOUCHER_NULL
== voucher
)
456 refs
= iv_reference(voucher
);
461 ipc_voucher_release(ipc_voucher_t voucher
)
463 if (IPC_VOUCHER_NULL
!= voucher
)
468 * Routine: ipc_voucher_notify
470 * Called whenever the Mach port system detects no-senders
471 * on the voucher port.
473 * Each time the send-right count goes positive, a no-senders
474 * notification is armed (and a voucher reference is donated).
475 * So, each notification that comes in must release a voucher
476 * reference. If more send rights have been added since it
477 * fired (asynchronously), they will be protected by a different
481 ipc_voucher_notify(mach_msg_header_t
*msg
)
483 mach_no_senders_notification_t
*notification
= (void *)msg
;
484 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
487 assert(ip_active(port
));
488 assert(IKOT_VOUCHER
== ip_kotype(port
));
489 iv
= (ipc_voucher_t
)port
->ip_kobject
;
491 ipc_voucher_release(iv
);
495 * Convert a voucher to a port.
498 convert_voucher_to_port(ipc_voucher_t voucher
)
500 ipc_port_t port
, send
;
502 if (IV_NULL
== voucher
)
505 assert(0 < voucher
->iv_refs
);
507 /* create a port if needed */
508 port
= voucher
->iv_port
;
509 if (!IP_VALID(port
)) {
510 port
= ipc_port_alloc_kernel();
511 assert(IP_VALID(port
));
512 ipc_kobject_set_atomically(port
, (ipc_kobject_t
) voucher
, IKOT_VOUCHER
);
514 /* If we lose the race, deallocate and pick up the other guy's port */
515 if (!OSCompareAndSwapPtr(IP_NULL
, port
, &voucher
->iv_port
)) {
516 ipc_port_dealloc_kernel(port
);
517 port
= voucher
->iv_port
;
518 assert(ip_kotype(port
) == IKOT_VOUCHER
);
519 assert(port
->ip_kobject
== (ipc_kobject_t
)voucher
);
524 assert(ip_active(port
));
525 send
= ipc_port_make_send_locked(port
);
527 if (1 == port
->ip_srights
) {
528 ipc_port_t old_notify
;
530 /* transfer our ref to the port, and arm the no-senders notification */
531 assert(IP_NULL
== port
->ip_nsrequest
);
532 ipc_port_nsrequest(port
, port
->ip_mscount
, ipc_port_make_sonce_locked(port
), &old_notify
);
534 assert(IP_NULL
== old_notify
);
536 /* piggyback on the existing port reference, so consume ours */
538 ipc_voucher_release(voucher
);
543 #define ivace_reset_data(ivace_elem, next_index) { \
544 (ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE; \
545 (ivace_elem)->ivace_refs = 0; \
546 (ivace_elem)->ivace_persist = 0; \
547 (ivace_elem)->ivace_made = 0; \
548 (ivace_elem)->ivace_free = TRUE; \
549 (ivace_elem)->ivace_releasing = FALSE; \
550 (ivace_elem)->ivace_layered = 0; \
551 (ivace_elem)->ivace_index = IV_HASH_END; \
552 (ivace_elem)->ivace_next = (next_index); \
555 #define ivace_copy_data(ivace_src_elem, ivace_dst_elem) { \
556 (ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
557 (ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs; \
558 (ivace_dst_elem)->ivace_persist = (ivace_src_elem)->ivace_persist; \
559 (ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made; \
560 (ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free; \
561 (ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered; \
562 (ivace_dst_elem)->ivace_releasing = (ivace_src_elem)->ivace_releasing; \
563 (ivace_dst_elem)->ivace_index = (ivace_src_elem)->ivace_index; \
564 (ivace_dst_elem)->ivace_next = (ivace_src_elem)->ivace_next; \
567 ipc_voucher_attr_control_t
568 ivac_alloc(iv_index_t key_index
)
570 ipc_voucher_attr_control_t ivac
;
575 ivac
= (ipc_voucher_attr_control_t
)zalloc(ipc_voucher_attr_control_zone
);
576 if (IVAC_NULL
== ivac
)
580 ivac
->ivac_is_growing
= FALSE
;
581 ivac
->ivac_port
= IP_NULL
;
583 /* start with just the inline table */
584 table
= (ivac_entry_t
) kalloc(IVAC_ENTRIES_MIN
* sizeof(ivac_entry
));
585 ivac
->ivac_table
= table
;
586 ivac
->ivac_table_size
= IVAC_ENTRIES_MIN
;
587 ivac
->ivac_init_table_size
= IVAC_ENTRIES_MIN
;
588 for (i
= 0; i
< ivac
->ivac_table_size
; i
++) {
589 ivace_reset_data(&table
[i
], i
+1);
592 /* the default table entry is never on freelist */
593 table
[0].ivace_next
= IV_HASH_END
;
594 table
[0].ivace_free
= FALSE
;
595 table
[i
-1].ivace_next
= IV_FREELIST_END
;
596 ivac
->ivac_freelist
= 1;
597 ivac_lock_init(ivac
);
598 ivac
->ivac_key_index
= key_index
;
604 ivac_dealloc(ipc_voucher_attr_control_t ivac
)
606 ipc_voucher_attr_manager_t ivam
= IVAM_NULL
;
607 iv_index_t key_index
= ivac
->ivac_key_index
;
608 ipc_port_t port
= ivac
->ivac_port
;
612 * If the control is in the global table, we
613 * have to remove it from there before we (re)confirm
614 * that the reference count is still zero.
617 if (ivac
->ivac_refs
> 0) {
622 /* take it out of the global table */
623 if (iv_global_table
[key_index
].ivgte_control
== ivac
) {
624 ivam
= iv_global_table
[key_index
].ivgte_manager
;
625 iv_global_table
[key_index
].ivgte_manager
= IVAM_NULL
;
626 iv_global_table
[key_index
].ivgte_control
= IVAC_NULL
;
627 iv_global_table
[key_index
].ivgte_key
= MACH_VOUCHER_ATTR_KEY_NONE
;
631 /* release the reference held on the resource manager */
632 if (IVAM_NULL
!= ivam
)
633 (ivam
->ivam_release
)(ivam
);
636 * if a port was allocated for this voucher,
637 * it must not have any remaining send rights,
638 * because the port's reference on the voucher
639 * is gone. We can just discard it now.
641 if (IP_VALID(port
)) {
642 assert(ip_active(port
));
643 assert(port
->ip_srights
== 0);
645 ipc_port_dealloc_kernel(port
);
649 * the resource manager's control reference and all references
650 * held by the specific value caches are gone, so free the
654 for (i
= 0; i
< ivac
->ivac_table_size
; i
++)
655 if (ivac
->ivac_table
[i
].ivace_refs
!= 0)
656 panic("deallocing a resource manager with live refs to its attr values\n");
658 kfree(ivac
->ivac_table
, ivac
->ivac_table_size
* sizeof(*ivac
->ivac_table
));
659 ivac_lock_destroy(ivac
);
660 zfree(ipc_voucher_attr_control_zone
, ivac
);
664 ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control
)
666 ivac_reference(control
);
670 ipc_voucher_attr_control_release(ipc_voucher_attr_control_t control
)
672 ivac_release(control
);
676 * Routine: convert_port_to_voucher_attr_control reference
678 * Convert from a port to a voucher attribute control.
679 * Doesn't consume the port ref; produces a voucher ref,
684 ipc_voucher_attr_control_t
685 convert_port_to_voucher_attr_control(
688 if (IP_VALID(port
)) {
689 ipc_voucher_attr_control_t ivac
= (ipc_voucher_attr_control_t
) port
->ip_kobject
;
692 * No need to lock because we have a reference on the
693 * port, and if it is a true voucher control port,
694 * that reference keeps the voucher bound to the port
697 if (ip_kotype(port
) != IKOT_VOUCHER_ATTR_CONTROL
)
700 assert(ip_active(port
));
702 ivac_reference(ivac
);
709 ipc_voucher_attr_control_notify(mach_msg_header_t
*msg
)
711 mach_no_senders_notification_t
*notification
= (void *)msg
;
712 ipc_port_t port
= notification
->not_header
.msgh_remote_port
;
713 ipc_voucher_attr_control_t ivac
;
715 assert(IKOT_VOUCHER_ATTR_CONTROL
== ip_kotype(port
));
717 assert(ip_active(port
));
719 /* if no new send rights, drop a control reference */
720 if (port
->ip_mscount
== notification
->not_count
) {
721 ivac
= (ipc_voucher_attr_control_t
)port
->ip_kobject
;
730 * Convert a voucher attr control to a port.
733 convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control
)
735 ipc_port_t port
, send
;
737 if (IVAC_NULL
== control
)
740 /* create a port if needed */
741 port
= control
->ivac_port
;
742 if (!IP_VALID(port
)) {
743 port
= ipc_port_alloc_kernel();
744 assert(IP_VALID(port
));
745 if (OSCompareAndSwapPtr(IP_NULL
, port
, &control
->ivac_port
)) {
747 ipc_kobject_set_atomically(port
, (ipc_kobject_t
) control
, IKOT_VOUCHER_ATTR_CONTROL
);
749 ipc_port_dealloc_kernel(port
);
750 port
= control
->ivac_port
;
752 assert(ip_kotype(port
) == IKOT_VOUCHER_ATTR_CONTROL
);
753 assert(port
->ip_kobject
== (ipc_kobject_t
)control
);
758 assert(ip_active(port
));
759 send
= ipc_port_make_send_locked(port
);
761 if (1 == port
->ip_srights
) {
762 ipc_port_t old_notify
;
764 /* transfer our ref to the port, and arm the no-senders notification */
765 assert(IP_NULL
== port
->ip_nsrequest
);
766 ipc_port_nsrequest(port
, port
->ip_mscount
, ipc_port_make_sonce_locked(port
), &old_notify
);
767 assert(IP_NULL
== old_notify
);
770 /* piggyback on the existing port reference, so consume ours */
772 ivac_release(control
);
778 * Look up the values for a given <key, index> pair.
782 iv_index_t key_index
,
783 iv_index_t value_index
,
784 mach_voucher_attr_value_handle_array_t values
,
785 mach_voucher_attr_value_handle_array_size_t
*count
)
787 ipc_voucher_attr_control_t ivac
;
790 if (IV_UNUSED_VALINDEX
== value_index
||
791 MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
<= key_index
) {
796 ivac
= iv_global_table
[key_index
].ivgte_control
;
797 assert(IVAC_NULL
!= ivac
);
800 * Get the entry and then the linked values.
803 assert(value_index
< ivac
->ivac_table_size
);
804 ivace
= &ivac
->ivac_table
[value_index
];
807 * TODO: support chained values (for effective vouchers).
809 assert(ivace
->ivace_refs
> 0);
810 values
[0] = ivace
->ivace_value
;
816 * ivac_grow_table - Allocate a bigger table of attribute values
818 * Conditions: ivac is locked on entry and again on return
821 ivac_grow_table(ipc_voucher_attr_control_t ivac
)
825 /* NOTE: do not modify *_table and *_size values once set */
826 ivac_entry_t new_table
= NULL
, old_table
= NULL
;
827 iv_index_t new_size
, old_size
;
829 if (ivac
->ivac_is_growing
) {
834 ivac
->ivac_is_growing
= 1;
835 if (ivac
->ivac_table_size
>= IVAC_ENTRIES_MAX
) {
836 panic("Cannot grow ipc space beyond IVAC_ENTRIES_MAX. Some process is leaking vouchers");
840 old_size
= ivac
->ivac_table_size
;
843 new_size
= old_size
* 2;
845 assert(new_size
> old_size
);
846 assert(new_size
< IVAC_ENTRIES_MAX
);
848 new_table
= kalloc(sizeof(ivac_entry
) * new_size
);
850 panic("Failed to grow ivac table to size %d\n", new_size
);
854 /* setup the free list for new entries */
855 for (i
= old_size
; i
< new_size
; i
++) {
856 ivace_reset_data(&new_table
[i
], i
+1);
861 for (i
= 0; i
< ivac
->ivac_table_size
; i
++){
862 ivace_copy_data(&ivac
->ivac_table
[i
], &new_table
[i
]);
865 old_table
= ivac
->ivac_table
;
867 ivac
->ivac_table
= new_table
;
868 ivac
->ivac_table_size
= new_size
;
870 /* adding new free entries at head of freelist */
871 ivac
->ivac_table
[new_size
- 1].ivace_next
= ivac
->ivac_freelist
;
872 ivac
->ivac_freelist
= old_size
;
873 ivac
->ivac_is_growing
= 0;
878 kfree(old_table
, old_size
* sizeof(ivac_entry
));
884 * ivace_reference_by_index
886 * Take an additional reference on the <key_index, val_index>
887 * cached value. It is assumed the caller already holds a
888 * reference to the same cached key-value pair.
891 ivace_reference_by_index(
892 iv_index_t key_index
,
893 iv_index_t val_index
)
895 ipc_voucher_attr_control_t ivac
;
898 if (IV_UNUSED_VALINDEX
== val_index
)
901 ivgt_lookup(key_index
, FALSE
, NULL
, &ivac
);
902 assert(IVAC_NULL
!= ivac
);
905 assert(val_index
< ivac
->ivac_table_size
);
906 ivace
= &ivac
->ivac_table
[val_index
];
908 assert(0xdeadc0dedeadc0de != ivace
->ivace_value
);
909 assert(0 < ivace
->ivace_refs
);
910 assert(!ivace
->ivace_free
);
912 /* Take ref only on non-persistent values */
913 if (!ivace
->ivace_persist
) {
921 * Look up the values for a given <key, index> pair.
923 * Consumes a reference on the passed voucher control.
924 * Either it is donated to a newly-created value cache
925 * or it is released (if we piggy back on an existing
926 * value cache entry).
929 ivace_reference_by_value(
930 ipc_voucher_attr_control_t ivac
,
931 mach_voucher_attr_value_handle_t value
,
932 mach_voucher_attr_value_flags_t flag
)
934 ivac_entry_t ivace
= IVACE_NULL
;
935 iv_index_t hash_index
;
938 if (IVAC_NULL
== ivac
) {
939 return IV_UNUSED_VALINDEX
;
944 hash_index
= IV_HASH_VAL(ivac
->ivac_init_table_size
, value
);
945 index
= ivac
->ivac_table
[hash_index
].ivace_index
;
946 while (index
!= IV_HASH_END
) {
947 assert(index
< ivac
->ivac_table_size
);
948 ivace
= &ivac
->ivac_table
[index
];
949 assert(!ivace
->ivace_free
);
951 if (ivace
->ivace_value
== value
)
954 assert(ivace
->ivace_next
!= index
);
955 index
= ivace
->ivace_next
;
959 if (index
!= IV_HASH_END
) {
960 /* only add reference on non-persistent value */
961 if (!ivace
->ivace_persist
) {
971 /* insert new entry in the table */
972 index
= ivac
->ivac_freelist
;
973 if (IV_FREELIST_END
== index
) {
975 ivac_grow_table(ivac
);
979 /* take the entry off the freelist */
980 ivace
= &ivac
->ivac_table
[index
];
981 ivac
->ivac_freelist
= ivace
->ivace_next
;
983 /* initialize the new entry */
984 ivace
->ivace_value
= value
;
985 ivace
->ivace_refs
= 1;
986 ivace
->ivace_made
= 1;
987 ivace
->ivace_free
= FALSE
;
988 ivace
->ivace_persist
= (flag
& MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST
) ? TRUE
: FALSE
;
990 /* insert the new entry in the proper hash chain */
991 ivace
->ivace_next
= ivac
->ivac_table
[hash_index
].ivace_index
;
992 ivac
->ivac_table
[hash_index
].ivace_index
= index
;
995 /* donated passed in ivac reference to new entry */
1001 * Release a reference on the given <key_index, value_index> pair.
1003 * Conditions: called with nothing locked, as it may cause
1004 * callouts and/or messaging to the resource
1007 static void ivace_release(
1008 iv_index_t key_index
,
1009 iv_index_t value_index
)
1011 ipc_voucher_attr_control_t ivac
;
1012 ipc_voucher_attr_manager_t ivam
;
1013 mach_voucher_attr_value_handle_t value
;
1014 mach_voucher_attr_value_reference_t made
;
1015 mach_voucher_attr_key_t key
;
1016 iv_index_t hash_index
;
1020 /* cant release the default value */
1021 if (IV_UNUSED_VALINDEX
== value_index
)
1024 ivgt_lookup(key_index
, FALSE
, &ivam
, &ivac
);
1025 assert(IVAC_NULL
!= ivac
);
1026 assert(IVAM_NULL
!= ivam
);
1029 assert(value_index
< ivac
->ivac_table_size
);
1030 ivace
= &ivac
->ivac_table
[value_index
];
1032 assert(0 < ivace
->ivace_refs
);
1034 /* cant release persistent values */
1035 if (ivace
->ivace_persist
) {
1040 if (0 < --ivace
->ivace_refs
) {
1045 key
= iv_index_to_key(key_index
);
1046 assert(MACH_VOUCHER_ATTR_KEY_NONE
!= key
);
1049 * if last return reply is still pending,
1050 * let it handle this later return when
1051 * the previous reply comes in.
1053 if (ivace
->ivace_releasing
) {
1058 /* claim releasing */
1059 ivace
->ivace_releasing
= TRUE
;
1060 value
= ivace
->ivace_value
;
1063 assert(value
== ivace
->ivace_value
);
1064 assert(!ivace
->ivace_free
);
1065 made
= ivace
->ivace_made
;
1068 /* callout to manager's release_value */
1069 kr
= (ivam
->ivam_release_value
)(ivam
, key
, value
, made
);
1071 /* recalculate entry address as table may have changed */
1073 ivace
= &ivac
->ivac_table
[value_index
];
1074 assert(value
== ivace
->ivace_value
);
1077 * new made values raced with this return. If the
1078 * manager OK'ed the prior release, we have to start
1079 * the made numbering over again (pretend the race
1080 * didn't happen). If the entry has zero refs again,
1081 * re-drive the release.
1083 if (ivace
->ivace_made
!= made
) {
1084 assert(made
< ivace
->ivace_made
);
1086 if (KERN_SUCCESS
== kr
)
1087 ivace
->ivace_made
-= made
;
1089 if (0 == ivace
->ivace_refs
)
1092 ivace
->ivace_releasing
= FALSE
;
1097 * If the manager returned FAILURE, someone took a
1098 * reference on the value but have not updated the ivace,
1099 * release the lock and return since thread who got
1100 * the new reference will update the ivace and will have
1101 * non-zero reference on the value.
1103 if (KERN_SUCCESS
!= kr
) {
1104 ivace
->ivace_releasing
= FALSE
;
1110 assert(0 == ivace
->ivace_refs
);
1113 * going away - remove entry from its hash
1114 * If its at the head of the hash bucket list (common), unchain
1115 * at the head. Otherwise walk the chain until the next points
1116 * at this entry, and remove it from the the list there.
1118 hash_index
= iv_hash_value(key_index
, value
);
1119 if (ivac
->ivac_table
[hash_index
].ivace_index
== value_index
) {
1120 ivac
->ivac_table
[hash_index
].ivace_index
= ivace
->ivace_next
;
1122 hash_index
= ivac
->ivac_table
[hash_index
].ivace_index
;
1123 assert(IV_HASH_END
!= hash_index
);
1124 while (ivac
->ivac_table
[hash_index
].ivace_next
!= value_index
) {
1125 hash_index
= ivac
->ivac_table
[hash_index
].ivace_next
;
1126 assert(IV_HASH_END
!= hash_index
);
1128 ivac
->ivac_table
[hash_index
].ivace_next
= ivace
->ivace_next
;
1131 /* Put this entry on the freelist */
1132 ivace
->ivace_value
= 0xdeadc0dedeadc0de;
1133 ivace
->ivace_releasing
= FALSE
;
1134 ivace
->ivace_free
= TRUE
;
1135 ivace
->ivace_made
= 0;
1136 ivace
->ivace_next
= ivac
->ivac_freelist
;
1137 ivac
->ivac_freelist
= value_index
;
1140 /* release the reference this value held on its cache control */
1150 * Lookup an entry in the global table from the context of a manager
1151 * registration. Adds a reference to the control to keep the results
1152 * around (if needed).
1154 * Because of the calling point, we can't be sure the manager is
1155 * [fully] registered yet. So, we must hold the global table lock
1156 * during the lookup to synchronize with in-parallel registrations
1157 * (and possible table growth).
1160 ivgt_lookup(iv_index_t key_index
,
1161 boolean_t take_reference
,
1162 ipc_voucher_attr_manager_t
*manager
,
1163 ipc_voucher_attr_control_t
*control
)
1165 ipc_voucher_attr_control_t ivac
;
1167 if (key_index
< MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
) {
1169 if (NULL
!= manager
)
1170 *manager
= iv_global_table
[key_index
].ivgte_manager
;
1171 ivac
= iv_global_table
[key_index
].ivgte_control
;
1172 if (IVAC_NULL
!= ivac
) {
1173 assert(key_index
== ivac
->ivac_key_index
);
1174 if (take_reference
) {
1175 assert(NULL
!= control
);
1176 ivac_reference(ivac
);
1180 if (NULL
!= control
)
1183 if (NULL
!= manager
)
1184 *manager
= IVAM_NULL
;
1185 if (NULL
!= control
)
1186 *control
= IVAC_NULL
;
1191 * Routine: ipc_replace_voucher_value
1193 * Replace the <voucher, key> value with the results of
1194 * running the supplied command through the resource
1195 * manager's get-value callback.
1197 * Nothing locked (may invoke user-space repeatedly).
1198 * Caller holds references on voucher and previous voucher.
1200 static kern_return_t
1201 ipc_replace_voucher_value(
1202 ipc_voucher_t voucher
,
1203 mach_voucher_attr_key_t key
,
1204 mach_voucher_attr_recipe_command_t command
,
1205 ipc_voucher_t prev_voucher
,
1206 mach_voucher_attr_content_t content
,
1207 mach_voucher_attr_content_size_t content_size
)
1209 mach_voucher_attr_value_handle_t previous_vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1210 mach_voucher_attr_value_handle_array_size_t previous_vals_count
;
1211 mach_voucher_attr_value_handle_t new_value
;
1212 mach_voucher_attr_value_flags_t new_flag
;
1213 ipc_voucher_t new_value_voucher
;
1214 ipc_voucher_attr_manager_t ivam
;
1215 ipc_voucher_attr_control_t ivac
;
1216 iv_index_t prev_val_index
;
1217 iv_index_t save_val_index
;
1218 iv_index_t val_index
;
1219 iv_index_t key_index
;
1223 * Get the manager for this key_index.
1224 * Returns a reference on the control.
1226 key_index
= iv_key_to_index(key
);
1227 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1228 if (IVAM_NULL
== ivam
)
1229 return KERN_INVALID_ARGUMENT
;
1231 /* save the current value stored in the forming voucher */
1232 save_val_index
= iv_lookup(voucher
, key_index
);
1235 * Get the previous value(s) for this key creation.
1236 * If a previous voucher is specified, they come from there.
1237 * Otherwise, they come from the intermediate values already
1238 * in the forming voucher.
1240 prev_val_index
= (IV_NULL
!= prev_voucher
) ?
1241 iv_lookup(prev_voucher
, key_index
) :
1243 ivace_lookup_values(key_index
, prev_val_index
,
1244 previous_vals
, &previous_vals_count
);
1246 /* Call out to resource manager to get new value */
1247 new_value_voucher
= IV_NULL
;
1248 kr
= (ivam
->ivam_get_value
)(
1250 previous_vals
, previous_vals_count
,
1251 content
, content_size
,
1252 &new_value
, &new_flag
, &new_value_voucher
);
1253 if (KERN_SUCCESS
!= kr
) {
1258 /* TODO: value insertion from returned voucher */
1259 if (IV_NULL
!= new_value_voucher
)
1260 iv_release(new_value_voucher
);
1263 * Find or create a slot in the table associated
1264 * with this attribute value. The ivac reference
1265 * is transferred to a new value, or consumed if
1266 * we find a matching existing value.
1268 val_index
= ivace_reference_by_value(ivac
, new_value
, new_flag
);
1269 iv_set(voucher
, key_index
, val_index
);
1272 * release saved old value from the newly forming voucher
1273 * This is saved until the end to avoid churning the
1274 * release logic in cases where the same value is returned
1275 * as was there before.
1277 ivace_release(key_index
, save_val_index
);
1279 return KERN_SUCCESS
;
1283 * Routine: ipc_directly_replace_voucher_value
1285 * Replace the <voucher, key> value with the value-handle
1286 * supplied directly by the attribute manager.
1289 * Caller holds references on voucher.
1290 * A made reference to the value-handle is donated by the caller.
1292 static kern_return_t
1293 ipc_directly_replace_voucher_value(
1294 ipc_voucher_t voucher
,
1295 mach_voucher_attr_key_t key
,
1296 mach_voucher_attr_value_handle_t new_value
)
1298 ipc_voucher_attr_manager_t ivam
;
1299 ipc_voucher_attr_control_t ivac
;
1300 iv_index_t save_val_index
;
1301 iv_index_t val_index
;
1302 iv_index_t key_index
;
1305 * Get the manager for this key_index.
1306 * Returns a reference on the control.
1308 key_index
= iv_key_to_index(key
);
1309 ivgt_lookup(key_index
, TRUE
, &ivam
, &ivac
);
1310 if (IVAM_NULL
== ivam
)
1311 return KERN_INVALID_ARGUMENT
;
1313 /* save the current value stored in the forming voucher */
1314 save_val_index
= iv_lookup(voucher
, key_index
);
1317 * Find or create a slot in the table associated
1318 * with this attribute value. The ivac reference
1319 * is transferred to a new value, or consumed if
1320 * we find a matching existing value.
1322 val_index
= ivace_reference_by_value(ivac
, new_value
,
1323 MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
);
1324 iv_set(voucher
, key_index
, val_index
);
1327 * release saved old value from the newly forming voucher
1328 * This is saved until the end to avoid churning the
1329 * release logic in cases where the same value is returned
1330 * as was there before.
1332 ivace_release(key_index
, save_val_index
);
1334 return KERN_SUCCESS
;
1337 static kern_return_t
1338 ipc_execute_voucher_recipe_command(
1339 ipc_voucher_t voucher
,
1340 mach_voucher_attr_key_t key
,
1341 mach_voucher_attr_recipe_command_t command
,
1342 ipc_voucher_t prev_iv
,
1343 mach_voucher_attr_content_t content
,
1344 mach_voucher_attr_content_size_t content_size
,
1347 iv_index_t prev_val_index
;
1348 iv_index_t val_index
;
1354 * MACH_VOUCHER_ATTR_COPY
1355 * Copy the attribute(s) from the previous voucher to the new
1356 * one. A wildcard key is an acceptable value - indicating a
1357 * desire to copy all the attribute values from the previous
1360 case MACH_VOUCHER_ATTR_COPY
:
1362 /* no recipe data on a copy */
1363 if (0 < content_size
)
1364 return KERN_INVALID_ARGUMENT
;
1366 /* nothing to copy from? - done */
1367 if (IV_NULL
== prev_iv
)
1368 return KERN_SUCCESS
;
1370 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1371 iv_index_t limit
, j
;
1373 /* reconcile possible difference in voucher sizes */
1374 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1375 prev_iv
->iv_table_size
:
1376 voucher
->iv_table_size
;
1378 /* wildcard matching */
1379 for (j
= 0; j
< limit
; j
++) {
1380 /* release old value being replaced */
1381 val_index
= iv_lookup(voucher
, j
);
1382 ivace_release(j
, val_index
);
1384 /* replace with reference to prev voucher's value */
1385 prev_val_index
= iv_lookup(prev_iv
, j
);
1386 ivace_reference_by_index(j
, prev_val_index
);
1387 iv_set(voucher
, j
, prev_val_index
);
1390 iv_index_t key_index
;
1392 /* copy just one key */
1393 key_index
= iv_key_to_index(key
);
1394 if (ivgt_keys_in_use
< key_index
)
1395 return KERN_INVALID_ARGUMENT
;
1397 /* release old value being replaced */
1398 val_index
= iv_lookup(voucher
, key_index
);
1399 ivace_release(key_index
, val_index
);
1401 /* replace with reference to prev voucher's value */
1402 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1403 ivace_reference_by_index(key_index
, prev_val_index
);
1404 iv_set(voucher
, key_index
, prev_val_index
);
1409 * MACH_VOUCHER_ATTR_REMOVE
1410 * Remove the attribute(s) from the under construction voucher.
1411 * A wildcard key is an acceptable value - indicating a desire
1412 * to remove all the attribute values set up so far in the voucher.
1413 * If a previous voucher is specified, only remove the value it
1414 * it matches the value in the previous voucher.
1416 case MACH_VOUCHER_ATTR_REMOVE
:
1417 /* no recipe data on a remove */
1418 if (0 < content_size
)
1419 return KERN_INVALID_ARGUMENT
;
1421 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1422 iv_index_t limit
, j
;
1424 /* reconcile possible difference in voucher sizes */
1425 limit
= (IV_NULL
== prev_iv
) ? voucher
->iv_table_size
:
1426 ((prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1427 prev_iv
->iv_table_size
: voucher
->iv_table_size
);
1429 /* wildcard matching */
1430 for (j
= 0; j
< limit
; j
++) {
1431 val_index
= iv_lookup(voucher
, j
);
1433 /* If not matched in previous, skip */
1434 if (IV_NULL
!= prev_iv
) {
1435 prev_val_index
= iv_lookup(prev_iv
, j
);
1436 if (val_index
!= prev_val_index
)
1439 /* release and clear */
1440 ivace_release(j
, val_index
);
1441 iv_set(voucher
, j
, IV_UNUSED_VALINDEX
);
1444 iv_index_t key_index
;
1446 /* copy just one key */
1447 key_index
= iv_key_to_index(key
);
1448 if (ivgt_keys_in_use
< key_index
)
1449 return KERN_INVALID_ARGUMENT
;
1451 val_index
= iv_lookup(voucher
, key_index
);
1453 /* If not matched in previous, skip */
1454 if (IV_NULL
!= prev_iv
) {
1455 prev_val_index
= iv_lookup(prev_iv
, key_index
);
1456 if (val_index
!= prev_val_index
)
1460 /* release and clear */
1461 ivace_release(key_index
, val_index
);
1462 iv_set(voucher
, key_index
, IV_UNUSED_VALINDEX
);
1467 * MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
1468 * Use key-privilege to set a value handle for the attribute directly,
1469 * rather than triggering a callback into the attribute manager to
1470 * interpret a recipe to generate the value handle.
1472 case MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
:
1474 mach_voucher_attr_value_handle_t new_value
;
1476 if (sizeof(mach_voucher_attr_value_handle_t
) != content_size
)
1477 return KERN_INVALID_ARGUMENT
;
1479 new_value
= *(mach_voucher_attr_value_handle_t
*)(void *)content
;
1480 kr
= ipc_directly_replace_voucher_value(voucher
,
1483 if (KERN_SUCCESS
!= kr
)
1486 return KERN_INVALID_CAPABILITY
;
1490 * MACH_VOUCHER_ATTR_REDEEM
1491 * Redeem the attribute(s) from the previous voucher for a possibly
1492 * new value in the new voucher. A wildcard key is an acceptable value,
1493 * indicating a desire to redeem all the values.
1495 case MACH_VOUCHER_ATTR_REDEEM
:
1497 if (MACH_VOUCHER_ATTR_KEY_ALL
== key
) {
1498 iv_index_t limit
, j
;
1500 /* reconcile possible difference in voucher sizes */
1501 if (IV_NULL
!= prev_iv
)
1502 limit
= (prev_iv
->iv_table_size
< voucher
->iv_table_size
) ?
1503 prev_iv
->iv_table_size
:
1504 voucher
->iv_table_size
;
1506 limit
= voucher
->iv_table_size
;
1508 /* wildcard matching */
1509 for (j
= 0; j
< limit
; j
++) {
1510 mach_voucher_attr_key_t j_key
;
1512 j_key
= iv_index_to_key(j
);
1514 /* skip non-existent managers */
1515 if (MACH_VOUCHER_ATTR_KEY_NONE
== j_key
)
1518 /* get the new value from redeem (skip empty previous) */
1519 kr
= ipc_replace_voucher_value(voucher
,
1525 if (KERN_SUCCESS
!= kr
)
1530 /* fall thru for single key redemption */
1534 * Replace the current value for the <voucher, key> pair with whatever
1535 * value the resource manager returns for the command and recipe
1536 * combination provided.
1539 kr
= ipc_replace_voucher_value(voucher
,
1545 if (KERN_SUCCESS
!= kr
)
1550 return KERN_SUCCESS
;
1554 * Routine: iv_checksum
1556 * Compute the voucher sum. This is more position-
1557 * relevant than many other checksums - important for
1558 * vouchers (arrays of low, oft-reused, indexes).
1560 static inline iv_index_t
1561 iv_checksum(ipc_voucher_t voucher
, boolean_t
*emptyp
)
1565 boolean_t empty
= TRUE
;
1566 if (0 < voucher
->iv_table_size
) {
1567 iv_index_t i
= voucher
->iv_table_size
- 1;
1570 iv_index_t v
= voucher
->iv_table
[i
];
1571 c
= c
<< 3 | c
>> (32 - 3); /* rotate */
1572 c
= ~c
; /* invert */
1574 c
+= v
; /* add in */
1586 * See if the set of values represented by this new voucher
1587 * already exist in another voucher. If so return a reference
1588 * to the existing voucher and deallocate the voucher provided.
1589 * Otherwise, insert this one in the hash and return it.
1591 * A voucher reference is donated on entry.
1593 * A voucher reference (may be different than on entry).
1595 static ipc_voucher_t
1596 iv_dedup(ipc_voucher_t new_iv
)
1603 sum
= iv_checksum(new_iv
, &empty
);
1605 /* If all values are default, that's the empty (NULL) voucher */
1607 iv_dealloc(new_iv
, FALSE
);
1611 hash
= IV_HASH_BUCKET(sum
);
1614 queue_iterate(&ivht_bucket
[hash
], iv
, ipc_voucher_t
, iv_hash_link
) {
1615 assert(iv
->iv_hash
== hash
);
1617 /* if not already deallocating and sums match... */
1618 if (0 < iv
->iv_refs
&& iv
->iv_sum
== sum
) {
1622 assert(iv
->iv_table_size
<= new_iv
->iv_table_size
);
1624 /* and common entries match... */
1625 for (i
= 0; i
< iv
->iv_table_size
; i
++)
1626 if (iv
->iv_table
[i
] != new_iv
->iv_table
[i
])
1628 if (i
< iv
->iv_table_size
)
1631 /* and all extra entries in new one are unused... */
1632 while (i
< new_iv
->iv_table_size
)
1633 if (new_iv
->iv_table
[i
++] != IV_UNUSED_VALINDEX
)
1635 if (i
< new_iv
->iv_table_size
)
1638 /* ... we found a match... */
1640 /* can we get a ref before it hits 0
1642 * This is thread safe. The reference is just an atomic
1643 * add. If the reference count is zero when we adjust it,
1644 * no other thread can have a reference to the voucher.
1645 * The dealloc code requires holding the ivht_lock, so
1646 * the voucher cannot be yanked out from under us.
1648 refs
= iv_reference(iv
);
1650 /* drats! going away. Put back to zero */
1657 /* referenced previous, so deallocate the new one */
1658 iv_dealloc(new_iv
, FALSE
);
1663 /* add the new voucher to the hash, and return it */
1664 new_iv
->iv_sum
= sum
;
1665 new_iv
->iv_hash
= hash
;
1666 queue_enter(&ivht_bucket
[hash
], new_iv
, ipc_voucher_t
, iv_hash_link
);
1671 * This code is disabled for KDEBUG_LEVEL_IST and KDEBUG_LEVEL_NONE
1673 #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD)
1674 if (kdebug_enable
& ~KDEBUG_ENABLE_PPT
) {
1675 uintptr_t voucher_addr
= VM_KERNEL_ADDRPERM((uintptr_t)new_iv
);
1676 uintptr_t attr_tracepoints_needed
= 0;
1678 if (ipc_voucher_trace_contents
) {
1680 * voucher_contents sizing is a bit more constrained
1681 * than might be obvious.
1683 * This is typically a uint8_t typed array. However,
1684 * we want to access it as a uintptr_t to efficiently
1685 * copyout the data in tracepoints.
1687 * This constrains the size to uintptr_t bytes, and
1688 * adds a minimimum alignment requirement equivalent
1691 * Further constraining the size is the fact that it
1692 * is copied out 4 uintptr_t chunks at a time. We do
1693 * NOT want to run off the end of the array and copyout
1694 * random stack data.
1696 * So the minimum size is 4 * sizeof(uintptr_t), and
1697 * the minimum alignment is uintptr_t aligned.
1700 #define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
1701 #define PAYLOAD_SIZE 1024
1703 _Static_assert(PAYLOAD_SIZE
% PAYLOAD_PER_TRACEPOINT
== 0, "size invariant violated");
1705 mach_voucher_attr_raw_recipe_array_size_t payload_size
= PAYLOAD_SIZE
;
1706 uintptr_t payload
[PAYLOAD_SIZE
/ sizeof(uintptr_t)];
1709 kr
= mach_voucher_extract_all_attr_recipes(new_iv
, (mach_voucher_attr_raw_recipe_array_t
)payload
, &payload_size
);
1710 if (KERN_SUCCESS
== kr
) {
1711 attr_tracepoints_needed
= (payload_size
+ PAYLOAD_PER_TRACEPOINT
- 1) / PAYLOAD_PER_TRACEPOINT
;
1714 * To prevent leaking data from the stack, we
1715 * need to zero data to the end of a tracepoint
1718 size_t remainder
= payload_size
% PAYLOAD_PER_TRACEPOINT
;
1720 bzero((uint8_t*)payload
+ payload_size
,
1721 PAYLOAD_PER_TRACEPOINT
- remainder
);
1725 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_CREATE
) | DBG_FUNC_NONE
,
1727 new_iv
->iv_table_size
, ivht_count
, payload_size
, 0);
1729 uintptr_t index
= 0;
1730 while (attr_tracepoints_needed
--) {
1731 KERNEL_DEBUG_CONSTANT1(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_CREATE_ATTR_DATA
) | DBG_FUNC_NONE
,
1740 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC
,MACH_IPC_VOUCHER_CREATE
) | DBG_FUNC_NONE
,
1742 new_iv
->iv_table_size
, ivht_count
, 0, 0);
1745 #endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
1751 * Routine: ipc_create_mach_voucher
1753 * Create a new mach voucher and initialize it with the
1754 * value(s) created by having the appropriate resource
1755 * managers interpret the supplied recipe commands and
1758 * Nothing locked (may invoke user-space repeatedly).
1759 * Caller holds references on previous vouchers.
1760 * Previous vouchers are passed as voucher indexes.
1763 ipc_create_mach_voucher(
1764 ipc_voucher_attr_raw_recipe_array_t recipes
,
1765 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1766 ipc_voucher_t
*new_voucher
)
1768 ipc_voucher_attr_recipe_t sub_recipe
;
1769 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1770 ipc_voucher_t voucher
;
1771 kern_return_t kr
= KERN_SUCCESS
;
1773 /* if nothing to do ... */
1774 if (0 == recipe_size
) {
1775 *new_voucher
= IV_NULL
;
1776 return KERN_SUCCESS
;
1779 /* allocate a voucher */
1780 voucher
= iv_alloc(ivgt_keys_in_use
);
1781 if (IV_NULL
== voucher
)
1782 return KERN_RESOURCE_SHORTAGE
;
1784 /* iterate over the recipe items */
1785 while (0 < recipe_size
- recipe_used
) {
1787 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1788 kr
= KERN_INVALID_ARGUMENT
;
1792 /* find the next recipe */
1793 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1794 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1795 kr
= KERN_INVALID_ARGUMENT
;
1798 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1800 kr
= ipc_execute_voucher_recipe_command(voucher
,
1802 sub_recipe
->command
,
1803 sub_recipe
->previous_voucher
,
1804 sub_recipe
->content
,
1805 sub_recipe
->content_size
,
1807 if (KERN_SUCCESS
!= kr
)
1811 if (KERN_SUCCESS
== kr
) {
1812 *new_voucher
= iv_dedup(voucher
);
1814 iv_dealloc(voucher
, FALSE
);
1815 *new_voucher
= IV_NULL
;
1821 * Routine: ipc_voucher_attr_control_create_mach_voucher
1823 * Create a new mach voucher and initialize it with the
1824 * value(s) created by having the appropriate resource
1825 * managers interpret the supplied recipe commands and
1828 * The resource manager control's privilege over its
1829 * particular key value is reflected on to the execution
1830 * code, allowing internal commands (like setting a
1831 * key value handle directly, rather than having to
1832 * create a recipe, that will generate a callback just
1836 * Nothing locked (may invoke user-space repeatedly).
1837 * Caller holds references on previous vouchers.
1838 * Previous vouchers are passed as voucher indexes.
1841 ipc_voucher_attr_control_create_mach_voucher(
1842 ipc_voucher_attr_control_t control
,
1843 ipc_voucher_attr_raw_recipe_array_t recipes
,
1844 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
,
1845 ipc_voucher_t
*new_voucher
)
1847 mach_voucher_attr_key_t control_key
;
1848 ipc_voucher_attr_recipe_t sub_recipe
;
1849 ipc_voucher_attr_recipe_size_t recipe_used
= 0;
1850 ipc_voucher_t voucher
= IV_NULL
;
1851 kern_return_t kr
= KERN_SUCCESS
;
1853 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
)
1854 return KERN_INVALID_CAPABILITY
;
1856 /* if nothing to do ... */
1857 if (0 == recipe_size
) {
1858 *new_voucher
= IV_NULL
;
1859 return KERN_SUCCESS
;
1862 /* allocate new voucher */
1863 voucher
= iv_alloc(ivgt_keys_in_use
);
1864 if (IV_NULL
== voucher
)
1865 return KERN_RESOURCE_SHORTAGE
;
1867 control_key
= iv_index_to_key(control
->ivac_key_index
);
1869 /* iterate over the recipe items */
1870 while (0 < recipe_size
- recipe_used
) {
1872 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
1873 kr
= KERN_INVALID_ARGUMENT
;
1877 /* find the next recipe */
1878 sub_recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
1879 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
1880 kr
= KERN_INVALID_ARGUMENT
;
1883 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
1885 kr
= ipc_execute_voucher_recipe_command(voucher
,
1887 sub_recipe
->command
,
1888 sub_recipe
->previous_voucher
,
1889 sub_recipe
->content
,
1890 sub_recipe
->content_size
,
1891 (sub_recipe
->key
== control_key
));
1892 if (KERN_SUCCESS
!= kr
)
1896 if (KERN_SUCCESS
== kr
) {
1897 *new_voucher
= iv_dedup(voucher
);
1899 *new_voucher
= IV_NULL
;
1900 iv_dealloc(voucher
, FALSE
);
1906 * ipc_register_well_known_mach_voucher_attr_manager
1908 * Register the resource manager responsible for a given key value.
1911 ipc_register_well_known_mach_voucher_attr_manager(
1912 ipc_voucher_attr_manager_t manager
,
1913 mach_voucher_attr_value_handle_t default_value
,
1914 mach_voucher_attr_key_t key
,
1915 ipc_voucher_attr_control_t
*control
)
1917 ipc_voucher_attr_control_t new_control
;
1918 iv_index_t key_index
;
1919 iv_index_t hash_index
;
1921 if (IVAM_NULL
== manager
)
1922 return KERN_INVALID_ARGUMENT
;
1924 key_index
= iv_key_to_index(key
);
1925 if (IV_UNUSED_KEYINDEX
== key_index
)
1926 return KERN_INVALID_ARGUMENT
;
1928 new_control
= ivac_alloc(key_index
);
1929 if (IVAC_NULL
== new_control
)
1930 return KERN_RESOURCE_SHORTAGE
;
1932 /* insert the default value into slot 0 */
1933 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_value
= default_value
;
1934 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_refs
= IVACE_REFS_MAX
;
1935 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_made
= IVACE_REFS_MAX
;
1936 new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_persist
= TRUE
;
1937 assert(IV_HASH_END
== new_control
->ivac_table
[IV_UNUSED_VALINDEX
].ivace_next
);
1940 if (IVAM_NULL
!= iv_global_table
[key_index
].ivgte_manager
) {
1942 ivac_release(new_control
);
1943 return KERN_INVALID_ARGUMENT
;
1946 /* fill in the global table slot for this key */
1947 iv_global_table
[key_index
].ivgte_manager
= manager
;
1948 iv_global_table
[key_index
].ivgte_control
= new_control
;
1949 iv_global_table
[key_index
].ivgte_key
= key
;
1951 /* insert the default value into the hash (in case it is returned later) */
1952 hash_index
= iv_hash_value(key_index
, default_value
);
1953 assert(IV_HASH_END
== new_control
->ivac_table
[hash_index
].ivace_index
);
1954 new_control
->ivac_table
[hash_index
].ivace_index
= IV_UNUSED_VALINDEX
;
1958 /* return the reference on the new cache control to the caller */
1959 *control
= new_control
;
1961 return KERN_SUCCESS
;
1965 * Routine: mach_voucher_extract_attr_content
1967 * Extract the content for a given <voucher, key> pair.
1969 * If a value other than the default is present for this
1970 * <voucher,key> pair, we need to contact the resource
1971 * manager to extract the content/meaning of the value(s)
1972 * present. Otherwise, return success (but no data).
1975 * Nothing locked - as it may upcall to user-space.
1976 * The caller holds a reference on the voucher.
1979 mach_voucher_extract_attr_content(
1980 ipc_voucher_t voucher
,
1981 mach_voucher_attr_key_t key
,
1982 mach_voucher_attr_content_t content
,
1983 mach_voucher_attr_content_size_t
*in_out_size
)
1985 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
1986 mach_voucher_attr_value_handle_array_size_t vals_count
;
1987 mach_voucher_attr_recipe_command_t command
;
1988 ipc_voucher_attr_manager_t manager
;
1989 iv_index_t value_index
;
1990 iv_index_t key_index
;
1994 if (IV_NULL
== voucher
)
1995 return KERN_INVALID_ARGUMENT
;
1997 key_index
= iv_key_to_index(key
);
1999 value_index
= iv_lookup(voucher
, key_index
);
2000 if (IV_UNUSED_VALINDEX
== value_index
) {
2002 return KERN_SUCCESS
;
2006 * Get the manager for this key_index. The
2007 * existence of a non-default value for this
2008 * slot within our voucher will keep the
2009 * manager referenced during the callout.
2011 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2012 if (IVAM_NULL
== manager
) {
2013 return KERN_INVALID_ARGUMENT
;
2017 * Get the value(s) to pass to the manager
2018 * for this value_index.
2020 ivace_lookup_values(key_index
, value_index
,
2022 assert(0 < vals_count
);
2024 /* callout to manager */
2026 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2029 content
, in_out_size
);
2034 * Routine: mach_voucher_extract_attr_recipe
2036 * Extract a recipe for a given <voucher, key> pair.
2038 * If a value other than the default is present for this
2039 * <voucher,key> pair, we need to contact the resource
2040 * manager to extract the content/meaning of the value(s)
2041 * present. Otherwise, return success (but no data).
2044 * Nothing locked - as it may upcall to user-space.
2045 * The caller holds a reference on the voucher.
2048 mach_voucher_extract_attr_recipe(
2049 ipc_voucher_t voucher
,
2050 mach_voucher_attr_key_t key
,
2051 mach_voucher_attr_raw_recipe_t raw_recipe
,
2052 mach_voucher_attr_raw_recipe_size_t
*in_out_size
)
2054 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2055 mach_voucher_attr_value_handle_array_size_t vals_count
;
2056 ipc_voucher_attr_manager_t manager
;
2057 mach_voucher_attr_recipe_t recipe
;
2058 iv_index_t value_index
;
2059 iv_index_t key_index
;
2063 if (IV_NULL
== voucher
)
2064 return KERN_INVALID_ARGUMENT
;
2066 key_index
= iv_key_to_index(key
);
2068 value_index
= iv_lookup(voucher
, key_index
);
2069 if (IV_UNUSED_VALINDEX
== value_index
) {
2071 return KERN_SUCCESS
;
2074 if (*in_out_size
< sizeof(*recipe
))
2075 return KERN_NO_SPACE
;
2077 recipe
= (mach_voucher_attr_recipe_t
)(void *)raw_recipe
;
2079 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2080 recipe
->previous_voucher
= MACH_VOUCHER_NAME_NULL
;
2081 recipe
->content_size
= *in_out_size
- sizeof(*recipe
);
2084 * Get the manager for this key_index. The
2085 * existence of a non-default value for this
2086 * slot within our voucher will keep the
2087 * manager referenced during the callout.
2089 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2090 if (IVAM_NULL
== manager
) {
2091 return KERN_INVALID_ARGUMENT
;
2095 * Get the value(s) to pass to the manager
2096 * for this value_index.
2098 ivace_lookup_values(key_index
, value_index
,
2100 assert(0 < vals_count
);
2102 /* callout to manager */
2103 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2106 recipe
->content
, &recipe
->content_size
);
2107 if (KERN_SUCCESS
== kr
) {
2108 assert(*in_out_size
- sizeof(*recipe
) >= recipe
->content_size
);
2109 *in_out_size
= sizeof(*recipe
) + recipe
->content_size
;
2118 * Routine: mach_voucher_extract_all_attr_recipes
2120 * Extract all the (non-default) contents for a given voucher,
2121 * building up a recipe that could be provided to a future
2122 * voucher creation call.
2124 * Nothing locked (may invoke user-space).
2125 * Caller holds a reference on the supplied voucher.
2128 mach_voucher_extract_all_attr_recipes(
2129 ipc_voucher_t voucher
,
2130 mach_voucher_attr_raw_recipe_array_t recipes
,
2131 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2133 mach_voucher_attr_recipe_size_t recipe_size
= *in_out_size
;
2134 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2135 iv_index_t key_index
;
2137 if (IV_NULL
== voucher
)
2138 return KERN_INVALID_ARGUMENT
;
2140 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2141 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2142 mach_voucher_attr_value_handle_array_size_t vals_count
;
2143 mach_voucher_attr_content_size_t content_size
;
2144 ipc_voucher_attr_manager_t manager
;
2145 mach_voucher_attr_recipe_t recipe
;
2146 mach_voucher_attr_key_t key
;
2147 iv_index_t value_index
;
2150 /* don't output anything for a default value */
2151 value_index
= iv_lookup(voucher
, key_index
);
2152 if (IV_UNUSED_VALINDEX
== value_index
)
2155 if (recipe_size
- recipe_used
< sizeof(*recipe
))
2156 return KERN_NO_SPACE
;
2159 * Get the manager for this key_index. The
2160 * existence of a non-default value for this
2161 * slot within our voucher will keep the
2162 * manager referenced during the callout.
2164 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2165 assert(IVAM_NULL
!= manager
);
2166 if (IVAM_NULL
== manager
) {
2170 recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2171 content_size
= recipe_size
- recipe_used
- sizeof(*recipe
);
2174 * Get the value(s) to pass to the manager
2175 * for this value_index.
2177 ivace_lookup_values(key_index
, value_index
,
2179 assert(0 < vals_count
);
2181 key
= iv_index_to_key(key_index
);
2184 recipe
->command
= MACH_VOUCHER_ATTR_NOOP
;
2185 recipe
->content_size
= content_size
;
2187 /* callout to manager */
2188 kr
= (manager
->ivam_extract_content
)(manager
, key
,
2191 recipe
->content
, &recipe
->content_size
);
2192 if (KERN_SUCCESS
!= kr
)
2195 assert(recipe
->content_size
<= content_size
);
2196 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2199 *in_out_size
= recipe_used
;
2200 return KERN_SUCCESS
;
2204 * Routine: mach_voucher_debug_info
2206 * Extract all the (non-default) contents for a given mach port name,
2207 * building up a recipe that could be provided to a future
2208 * voucher creation call.
2210 * Nothing locked (may invoke user-space).
2211 * Caller may not hold a reference on the supplied voucher.
2213 #if !(DEVELOPMENT || DEBUG)
2215 mach_voucher_debug_info(
2216 ipc_space_t __unused space
,
2217 mach_port_name_t __unused voucher_name
,
2218 mach_voucher_attr_raw_recipe_array_t __unused recipes
,
2219 mach_voucher_attr_raw_recipe_array_size_t __unused
*in_out_size
)
2221 return KERN_NOT_SUPPORTED
;
2225 mach_voucher_debug_info(
2227 mach_port_name_t voucher_name
,
2228 mach_voucher_attr_raw_recipe_array_t recipes
,
2229 mach_voucher_attr_raw_recipe_array_size_t
*in_out_size
)
2231 ipc_voucher_t voucher
= IPC_VOUCHER_NULL
;
2233 ipc_port_t port
= MACH_PORT_NULL
;
2235 if (!MACH_PORT_VALID(voucher_name
)) {
2236 return KERN_INVALID_ARGUMENT
;
2239 kr
= ipc_port_translate_send(space
, voucher_name
, &port
);
2240 if (KERN_SUCCESS
!= kr
)
2241 return KERN_INVALID_ARGUMENT
;
2243 voucher
= convert_port_to_voucher(port
);
2247 kr
= mach_voucher_extract_all_attr_recipes(voucher
, recipes
, in_out_size
);
2248 ipc_voucher_release(voucher
);
2252 return KERN_FAILURE
;
2257 * Routine: mach_voucher_attr_command
2259 * Invoke an attribute-specific command through this voucher.
2261 * The voucher layout, membership, etc... is not altered
2262 * through the execution of this command.
2265 * Nothing locked - as it may upcall to user-space.
2266 * The caller holds a reference on the voucher.
2269 mach_voucher_attr_command(
2270 ipc_voucher_t voucher
,
2271 mach_voucher_attr_key_t key
,
2272 mach_voucher_attr_command_t command
,
2273 mach_voucher_attr_content_t in_content
,
2274 mach_voucher_attr_content_size_t in_content_size
,
2275 mach_voucher_attr_content_t out_content
,
2276 mach_voucher_attr_content_size_t
*out_content_size
)
2278 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2279 mach_voucher_attr_value_handle_array_size_t vals_count
;
2280 ipc_voucher_attr_manager_t manager
;
2281 ipc_voucher_attr_control_t control
;
2282 iv_index_t value_index
;
2283 iv_index_t key_index
;
2287 if (IV_NULL
== voucher
)
2288 return KERN_INVALID_ARGUMENT
;
2290 key_index
= iv_key_to_index(key
);
2293 * Get the manager for this key_index.
2294 * Allowing commands against the default value
2295 * for an attribute means that we have to hold
2296 * reference on the attribute manager control
2297 * to keep the manager around during the command
2300 ivgt_lookup(key_index
, TRUE
, &manager
, &control
);
2301 if (IVAM_NULL
== manager
) {
2302 return KERN_INVALID_ARGUMENT
;
2306 * Get the values for this <voucher, key> pair
2307 * to pass to the attribute manager. It is still
2308 * permissible to execute a command against the
2309 * default value (empty value array).
2311 value_index
= iv_lookup(voucher
, key_index
);
2312 ivace_lookup_values(key_index
, value_index
,
2315 /* callout to manager */
2316 kr
= (manager
->ivam_command
)(manager
, key
,
2319 in_content
, in_content_size
,
2320 out_content
, out_content_size
);
2322 /* release reference on control */
2323 ivac_release(control
);
2329 * Routine: mach_voucher_attr_control_get_values
2331 * For a given voucher, get the value handle associated with the
2332 * specified attribute manager.
2335 mach_voucher_attr_control_get_values(
2336 ipc_voucher_attr_control_t control
,
2337 ipc_voucher_t voucher
,
2338 mach_voucher_attr_value_handle_array_t out_values
,
2339 mach_voucher_attr_value_handle_array_size_t
*in_out_size
)
2341 iv_index_t key_index
, value_index
;
2343 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
)
2344 return KERN_INVALID_CAPABILITY
;
2346 if (IV_NULL
== voucher
)
2347 return KERN_INVALID_ARGUMENT
;
2349 if (0 == *in_out_size
)
2350 return KERN_SUCCESS
;
2352 key_index
= control
->ivac_key_index
;
2354 assert(0 < voucher
->iv_refs
);
2355 value_index
= iv_lookup(voucher
, key_index
);
2356 ivace_lookup_values(key_index
, value_index
,
2357 out_values
, in_out_size
);
2358 return KERN_SUCCESS
;
2362 * Routine: mach_voucher_attr_control_create_mach_voucher
2364 * Create a new mach voucher and initialize it by processing the
2365 * supplied recipe(s).
2367 * Coming in on the attribute control port denotes special privileges
2368 * over they key associated with the control port.
2370 * Coming in from user-space, each recipe item will have a previous
2371 * recipe port name that needs to be converted to a voucher. Because
2372 * we can't rely on the port namespace to hold a reference on each
2373 * previous voucher port for the duration of processing that command,
2374 * we have to convert the name to a voucher reference and release it
2375 * after the command processing is done.
2378 mach_voucher_attr_control_create_mach_voucher(
2379 ipc_voucher_attr_control_t control
,
2380 mach_voucher_attr_raw_recipe_array_t recipes
,
2381 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2382 ipc_voucher_t
*new_voucher
)
2384 mach_voucher_attr_key_t control_key
;
2385 mach_voucher_attr_recipe_t sub_recipe
;
2386 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2387 ipc_voucher_t voucher
= IV_NULL
;
2388 kern_return_t kr
= KERN_SUCCESS
;
2390 if (IPC_VOUCHER_ATTR_CONTROL_NULL
== control
)
2391 return KERN_INVALID_CAPABILITY
;
2393 /* if nothing to do ... */
2394 if (0 == recipe_size
) {
2395 *new_voucher
= IV_NULL
;
2396 return KERN_SUCCESS
;
2399 /* allocate new voucher */
2400 voucher
= iv_alloc(ivgt_keys_in_use
);
2401 if (IV_NULL
== voucher
)
2402 return KERN_RESOURCE_SHORTAGE
;
2404 control_key
= iv_index_to_key(control
->ivac_key_index
);
2406 /* iterate over the recipe items */
2407 while (0 < recipe_size
- recipe_used
) {
2408 ipc_voucher_t prev_iv
;
2410 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2411 kr
= KERN_INVALID_ARGUMENT
;
2415 /* find the next recipe */
2416 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2417 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2418 kr
= KERN_INVALID_ARGUMENT
;
2421 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2423 /* convert voucher port name (current space) into a voucher reference */
2424 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2425 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2426 kr
= KERN_INVALID_CAPABILITY
;
2430 kr
= ipc_execute_voucher_recipe_command(voucher
,
2432 sub_recipe
->command
,
2434 sub_recipe
->content
,
2435 sub_recipe
->content_size
,
2436 (sub_recipe
->key
== control_key
));
2437 ipc_voucher_release(prev_iv
);
2439 if (KERN_SUCCESS
!= kr
)
2443 if (KERN_SUCCESS
== kr
) {
2444 *new_voucher
= iv_dedup(voucher
);
2446 *new_voucher
= IV_NULL
;
2447 iv_dealloc(voucher
, FALSE
);
2453 * Routine: host_create_mach_voucher
2455 * Create a new mach voucher and initialize it by processing the
2456 * supplied recipe(s).
2458 * Comming in from user-space, each recipe item will have a previous
2459 * recipe port name that needs to be converted to a voucher. Because
2460 * we can't rely on the port namespace to hold a reference on each
2461 * previous voucher port for the duration of processing that command,
2462 * we have to convert the name to a voucher reference and release it
2463 * after the command processing is done.
2466 host_create_mach_voucher(
2468 mach_voucher_attr_raw_recipe_array_t recipes
,
2469 mach_voucher_attr_raw_recipe_size_t recipe_size
,
2470 ipc_voucher_t
*new_voucher
)
2472 mach_voucher_attr_recipe_t sub_recipe
;
2473 mach_voucher_attr_recipe_size_t recipe_used
= 0;
2474 ipc_voucher_t voucher
= IV_NULL
;
2475 kern_return_t kr
= KERN_SUCCESS
;
2477 if (host
== HOST_NULL
)
2478 return KERN_INVALID_ARGUMENT
;
2480 /* if nothing to do ... */
2481 if (0 == recipe_size
) {
2482 *new_voucher
= IV_NULL
;
2483 return KERN_SUCCESS
;
2486 /* allocate new voucher */
2487 voucher
= iv_alloc(ivgt_keys_in_use
);
2488 if (IV_NULL
== voucher
)
2489 return KERN_RESOURCE_SHORTAGE
;
2491 /* iterate over the recipe items */
2492 while (0 < recipe_size
- recipe_used
) {
2493 ipc_voucher_t prev_iv
;
2495 if (recipe_size
- recipe_used
< sizeof(*sub_recipe
)) {
2496 kr
= KERN_INVALID_ARGUMENT
;
2500 /* find the next recipe */
2501 sub_recipe
= (mach_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2502 if (recipe_size
- recipe_used
- sizeof(*sub_recipe
) < sub_recipe
->content_size
) {
2503 kr
= KERN_INVALID_ARGUMENT
;
2506 recipe_used
+= sizeof(*sub_recipe
) + sub_recipe
->content_size
;
2508 /* convert voucher port name (current space) into a voucher reference */
2509 prev_iv
= convert_port_name_to_voucher(sub_recipe
->previous_voucher
);
2510 if (MACH_PORT_NULL
!= sub_recipe
->previous_voucher
&& IV_NULL
== prev_iv
) {
2511 kr
= KERN_INVALID_CAPABILITY
;
2515 kr
= ipc_execute_voucher_recipe_command(voucher
,
2517 sub_recipe
->command
,
2519 sub_recipe
->content
,
2520 sub_recipe
->content_size
,
2522 ipc_voucher_release(prev_iv
);
2524 if (KERN_SUCCESS
!= kr
)
2528 if (KERN_SUCCESS
== kr
) {
2529 *new_voucher
= iv_dedup(voucher
);
2531 *new_voucher
= IV_NULL
;
2532 iv_dealloc(voucher
, FALSE
);
2538 * Routine: host_register_well_known_mach_voucher_attr_manager
2540 * Register the user-level resource manager responsible for a given
2543 * The manager port passed in has to be converted/wrapped
2544 * in an ipc_voucher_attr_manager_t structure and then call the
2545 * internal variant. We have a generic ipc voucher manager
2546 * type that implements a MIG proxy out to user-space just for
2550 host_register_well_known_mach_voucher_attr_manager(
2552 mach_voucher_attr_manager_t __unused manager
,
2553 mach_voucher_attr_value_handle_t __unused default_value
,
2554 mach_voucher_attr_key_t __unused key
,
2555 ipc_voucher_attr_control_t __unused
*control
)
2557 if (HOST_NULL
== host
)
2558 return KERN_INVALID_HOST
;
2561 return KERN_NOT_SUPPORTED
;
2564 * Allocate a mig_voucher_attr_manager_t that provides the
2565 * MIG proxy functions for the three manager callbacks and
2566 * store the port right in there.
2568 * If the user-space manager dies, we'll detect it on our
2569 * next upcall, and cleanup the proxy at that point.
2571 mig_voucher_attr_manager_t proxy
;
2574 proxy
= mvam_alloc(manager
);
2576 kr
= ipc_register_well_known_mach_voucher_attr_manager(&proxy
->mvam_manager
,
2580 if (KERN_SUCCESS
!= kr
)
2581 mvam_release(proxy
);
2588 * Routine: host_register_mach_voucher_attr_manager
2590 * Register the user-space resource manager and return a
2591 * dynamically allocated key.
2593 * Wrap the supplied port with the MIG proxy ipc
2594 * voucher resource manager, and then call the internal
2598 host_register_mach_voucher_attr_manager(
2600 mach_voucher_attr_manager_t __unused manager
,
2601 mach_voucher_attr_value_handle_t __unused default_value
,
2602 mach_voucher_attr_key_t __unused
*key
,
2603 ipc_voucher_attr_control_t __unused
*control
)
2605 if (HOST_NULL
== host
)
2606 return KERN_INVALID_HOST
;
2608 return KERN_NOT_SUPPORTED
;
2612 * Routine: ipc_voucher_send_preprocessing
2614 * Processing of the voucher in the kmsg before sending it.
2615 * Currently use to switch PERSONA_TOKEN in case of process with
2616 * no com.apple.private.personas.propagate entitlement.
2619 ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg
)
2621 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2622 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2623 sizeof(ipc_voucher_attr_recipe_data_t
);
2624 ipc_voucher_t pre_processed_voucher
;
2625 ipc_voucher_t voucher_to_send
;
2627 int need_preprocessing
= FALSE
;
2629 if (!IP_VALID(kmsg
->ikm_voucher
) || current_task() == kernel_task
) {
2633 /* setup recipe for preprocessing of all the attributes. */
2634 pre_processed_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2636 kr
= ipc_voucher_prepare_processing_recipe(pre_processed_voucher
,
2637 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2638 &recipe_size
, MACH_VOUCHER_ATTR_SEND_PREPROCESS
,
2639 IVAM_FLAGS_SUPPORT_SEND_PREPROCESS
, &need_preprocessing
);
2641 assert(KERN_SUCCESS
== kr
);
2643 * Only do send preprocessing if the voucher needs any pre processing.
2645 if (need_preprocessing
) {
2646 kr
= ipc_create_mach_voucher(recipes
,
2649 assert(KERN_SUCCESS
== kr
);
2650 ipc_port_release_send(kmsg
->ikm_voucher
);
2651 kmsg
->ikm_voucher
= convert_voucher_to_port(voucher_to_send
);
2656 * Routine: ipc_voucher_receive_postprocessing
2658 * Redeems the voucher attached to the kmsg.
2660 * Although it is possible to call ipc_importance_receive
2661 * here, it is called in mach_msg_receive_results and not here
2662 * in order to maintain symmetry with ipc_voucher_send_preprocessing.
2665 ipc_voucher_receive_postprocessing(
2667 mach_msg_option_t option
)
2669 uint8_t recipes
[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) * sizeof(ipc_voucher_attr_recipe_data_t
)];
2670 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN
+ 1) *
2671 sizeof(ipc_voucher_attr_recipe_data_t
);
2672 ipc_voucher_t recv_voucher
;
2673 ipc_voucher_t sent_voucher
;
2675 int need_postprocessing
= FALSE
;
2677 if ((option
& MACH_RCV_VOUCHER
) == 0 || (!IP_VALID(kmsg
->ikm_voucher
)) ||
2678 current_task() == kernel_task
) {
2682 /* setup recipe for auto redeem of all the attributes. */
2683 sent_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2685 kr
= ipc_voucher_prepare_processing_recipe(sent_voucher
,
2686 (mach_voucher_attr_raw_recipe_array_t
)recipes
,
2687 &recipe_size
, MACH_VOUCHER_ATTR_AUTO_REDEEM
,
2688 IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS
, &need_postprocessing
);
2690 assert(KERN_SUCCESS
== kr
);
2693 * Only do receive postprocessing if the voucher needs any post processing.
2695 if (need_postprocessing
) {
2696 kr
= ipc_create_mach_voucher(recipes
,
2699 assert(KERN_SUCCESS
== kr
);
2700 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
2701 kmsg
->ikm_header
->msgh_bits
|= (MACH_MSG_TYPE_MOVE_SEND
<< 16);
2702 ipc_port_release_send(kmsg
->ikm_voucher
);
2703 kmsg
->ikm_voucher
= convert_voucher_to_port(recv_voucher
);
2708 * Routine: ipc_voucher_prepare_processing_recipe
2710 * Check if the given voucher has an attribute which supports
2711 * the given flag and prepare a recipe to apply that supported
2714 static kern_return_t
2715 ipc_voucher_prepare_processing_recipe(
2716 ipc_voucher_t voucher
,
2717 ipc_voucher_attr_raw_recipe_array_t recipes
,
2718 ipc_voucher_attr_raw_recipe_array_size_t
*in_out_size
,
2719 mach_voucher_attr_recipe_command_t command
,
2720 ipc_voucher_attr_manager_flags flags
,
2721 int *need_processing
)
2723 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= *in_out_size
;
2724 ipc_voucher_attr_raw_recipe_array_size_t recipe_used
= 0;
2725 iv_index_t key_index
;
2726 ipc_voucher_attr_recipe_t recipe
;
2728 if (IV_NULL
== voucher
)
2729 return KERN_INVALID_ARGUMENT
;
2731 /* Setup a recipe to copy all attributes. */
2732 if (recipe_size
< sizeof(*recipe
))
2733 return KERN_NO_SPACE
;
2735 *need_processing
= FALSE
;
2736 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2737 recipe
->key
= MACH_VOUCHER_ATTR_KEY_ALL
;
2738 recipe
->command
= MACH_VOUCHER_ATTR_COPY
;
2739 recipe
->previous_voucher
= voucher
;
2740 recipe
->content_size
= 0;
2741 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2743 for (key_index
= 0; key_index
< voucher
->iv_table_size
; key_index
++) {
2744 ipc_voucher_attr_manager_t manager
;
2745 mach_voucher_attr_key_t key
;
2746 iv_index_t value_index
;
2748 /* don't output anything for a default value */
2749 value_index
= iv_lookup(voucher
, key_index
);
2750 if (IV_UNUSED_VALINDEX
== value_index
)
2753 if (recipe_size
- recipe_used
< sizeof(*recipe
))
2754 return KERN_NO_SPACE
;
2756 recipe
= (ipc_voucher_attr_recipe_t
)(void *)&recipes
[recipe_used
];
2759 * Get the manager for this key_index. The
2760 * existence of a non-default value for this
2761 * slot within our voucher will keep the
2762 * manager referenced during the callout.
2764 ivgt_lookup(key_index
, FALSE
, &manager
, NULL
);
2765 assert(IVAM_NULL
!= manager
);
2766 if (IVAM_NULL
== manager
) {
2770 /* Check if the supported flag is set in the manager */
2771 if ((manager
->ivam_flags
& flags
) == 0)
2774 key
= iv_index_to_key(key_index
);
2777 recipe
->command
= command
;
2778 recipe
->content_size
= 0;
2779 recipe
->previous_voucher
= voucher
;
2781 recipe_used
+= sizeof(*recipe
) + recipe
->content_size
;
2782 *need_processing
= TRUE
;
2785 *in_out_size
= recipe_used
;
2786 return KERN_SUCCESS
;
2789 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2792 * Build-in a simple User Data Resource Manager
2794 #define USER_DATA_MAX_DATA (16*1024)
2796 struct user_data_value_element
{
2797 mach_voucher_attr_value_reference_t e_made
;
2798 mach_voucher_attr_content_size_t e_size
;
2801 queue_chain_t e_hash_link
;
2805 typedef struct user_data_value_element
*user_data_element_t
;
2808 * User Data Voucher Hash Table
2810 #define USER_DATA_HASH_BUCKETS 127
2811 #define USER_DATA_HASH_BUCKET(x) ((x) % USER_DATA_HASH_BUCKETS)
2813 static queue_head_t user_data_bucket
[USER_DATA_HASH_BUCKETS
];
2814 static lck_spin_t user_data_lock_data
;
2816 #define user_data_lock_init() \
2817 lck_spin_init(&user_data_lock_data, &ipc_lck_grp, &ipc_lck_attr)
2818 #define user_data_lock_destroy() \
2819 lck_spin_destroy(&user_data_lock_data, &ipc_lck_grp)
2820 #define user_data_lock() \
2821 lck_spin_lock(&user_data_lock_data)
2822 #define user_data_lock_try() \
2823 lck_spin_try_lock(&user_data_lock_data)
2824 #define user_data_unlock() \
2825 lck_spin_unlock(&user_data_lock_data)
2827 static kern_return_t
2828 user_data_release_value(
2829 ipc_voucher_attr_manager_t manager
,
2830 mach_voucher_attr_key_t key
,
2831 mach_voucher_attr_value_handle_t value
,
2832 mach_voucher_attr_value_reference_t sync
);
2834 static kern_return_t
2835 user_data_get_value(
2836 ipc_voucher_attr_manager_t manager
,
2837 mach_voucher_attr_key_t key
,
2838 mach_voucher_attr_recipe_command_t command
,
2839 mach_voucher_attr_value_handle_array_t prev_values
,
2840 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
2841 mach_voucher_attr_content_t content
,
2842 mach_voucher_attr_content_size_t content_size
,
2843 mach_voucher_attr_value_handle_t
*out_value
,
2844 mach_voucher_attr_value_flags_t
*out_flags
,
2845 ipc_voucher_t
*out_value_voucher
);
2847 static kern_return_t
2848 user_data_extract_content(
2849 ipc_voucher_attr_manager_t manager
,
2850 mach_voucher_attr_key_t key
,
2851 mach_voucher_attr_value_handle_array_t values
,
2852 mach_voucher_attr_value_handle_array_size_t value_count
,
2853 mach_voucher_attr_recipe_command_t
*out_command
,
2854 mach_voucher_attr_content_t out_content
,
2855 mach_voucher_attr_content_size_t
*in_out_content_size
);
2857 static kern_return_t
2859 ipc_voucher_attr_manager_t manager
,
2860 mach_voucher_attr_key_t key
,
2861 mach_voucher_attr_value_handle_array_t values
,
2862 mach_msg_type_number_t value_count
,
2863 mach_voucher_attr_command_t command
,
2864 mach_voucher_attr_content_t in_content
,
2865 mach_voucher_attr_content_size_t in_content_size
,
2866 mach_voucher_attr_content_t out_content
,
2867 mach_voucher_attr_content_size_t
*out_content_size
);
2871 ipc_voucher_attr_manager_t manager
);
2873 struct ipc_voucher_attr_manager user_data_manager
= {
2874 .ivam_release_value
= user_data_release_value
,
2875 .ivam_get_value
= user_data_get_value
,
2876 .ivam_extract_content
= user_data_extract_content
,
2877 .ivam_command
= user_data_command
,
2878 .ivam_release
= user_data_release
,
2879 .ivam_flags
= IVAM_FLAGS_NONE
,
2882 ipc_voucher_attr_control_t user_data_control
;
2883 ipc_voucher_attr_control_t test_control
;
2885 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) && defined(MACH_VOUCHER_ATTR_KEY_TEST)
2886 #define USER_DATA_ASSERT_KEY(key) \
2887 assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key) || \
2888 MACH_VOUCHER_ATTR_KEY_TEST == (key));
2889 #elif defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
2890 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key))
2892 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_TEST == (key))
2896 * Routine: user_data_release_value
2898 * Release a made reference on a specific value managed by
2899 * this voucher attribute manager.
2901 * Must remove the element associated with this value from
2902 * the hash if this is the last know made reference.
2904 static kern_return_t
2905 user_data_release_value(
2906 ipc_voucher_attr_manager_t __assert_only manager
,
2907 mach_voucher_attr_key_t __assert_only key
,
2908 mach_voucher_attr_value_handle_t value
,
2909 mach_voucher_attr_value_reference_t sync
)
2911 user_data_element_t elem
;
2914 assert (&user_data_manager
== manager
);
2915 USER_DATA_ASSERT_KEY(key
);
2917 elem
= (user_data_element_t
)value
;
2918 hash
= elem
->e_hash
;
2921 if (sync
== elem
->e_made
) {
2922 queue_remove(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
);
2924 kfree(elem
, sizeof(*elem
) + elem
->e_size
);
2925 return KERN_SUCCESS
;
2927 assert(sync
< elem
->e_made
);
2930 return KERN_FAILURE
;
2934 * Routine: user_data_checksum
2936 * Provide a rudimentary checksum for the data presented
2937 * to these voucher attribute managers.
2941 mach_voucher_attr_content_t content
,
2942 mach_voucher_attr_content_size_t content_size
)
2944 mach_voucher_attr_content_size_t i
;
2945 iv_index_t cksum
= 0;
2947 for(i
= 0; i
< content_size
; i
++, content
++) {
2948 cksum
= (cksum
<< 8) ^ (cksum
+ *(unsigned char *)content
);
2955 * Routine: user_data_dedup
2957 * See if the content represented by this request already exists
2958 * in another user data element. If so return a made reference
2959 * to the existing element. Otherwise, create a new element and
2960 * return that (after inserting it in the hash).
2964 * A made reference on the user_data_element_t
2966 static user_data_element_t
2968 mach_voucher_attr_content_t content
,
2969 mach_voucher_attr_content_size_t content_size
)
2973 user_data_element_t elem
;
2974 user_data_element_t alloc
= NULL
;
2976 sum
= user_data_checksum(content
, content_size
);
2977 hash
= USER_DATA_HASH_BUCKET(sum
);
2981 queue_iterate(&user_data_bucket
[hash
], elem
, user_data_element_t
, e_hash_link
) {
2982 assert(elem
->e_hash
== hash
);
2984 /* if sums match... */
2985 if (elem
->e_sum
== sum
&& elem
->e_size
== content_size
) {
2988 /* and all data matches */
2989 for (i
= 0; i
< content_size
; i
++)
2990 if (elem
->e_data
[i
] != content
[i
])
2992 if (i
< content_size
)
2995 /* ... we found a match... */
3001 kfree(alloc
, sizeof(*alloc
) + content_size
);
3007 if (NULL
== alloc
) {
3010 alloc
= (user_data_element_t
)kalloc(sizeof(*alloc
) + content_size
);
3012 alloc
->e_size
= content_size
;
3014 alloc
->e_hash
= hash
;
3015 memcpy(alloc
->e_data
, content
, content_size
);
3019 queue_enter(&user_data_bucket
[hash
], alloc
, user_data_element_t
, e_hash_link
);
3025 static kern_return_t
3026 user_data_get_value(
3027 ipc_voucher_attr_manager_t __assert_only manager
,
3028 mach_voucher_attr_key_t __assert_only key
,
3029 mach_voucher_attr_recipe_command_t command
,
3030 mach_voucher_attr_value_handle_array_t prev_values
,
3031 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3032 mach_voucher_attr_content_t content
,
3033 mach_voucher_attr_content_size_t content_size
,
3034 mach_voucher_attr_value_handle_t
*out_value
,
3035 mach_voucher_attr_value_flags_t
*out_flags
,
3036 ipc_voucher_t
*out_value_voucher
)
3038 user_data_element_t elem
;
3040 assert (&user_data_manager
== manager
);
3041 USER_DATA_ASSERT_KEY(key
);
3043 /* never an out voucher */
3044 *out_value_voucher
= IPC_VOUCHER_NULL
;
3045 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
3049 case MACH_VOUCHER_ATTR_REDEEM
:
3051 /* redeem of previous values is the value */
3052 if (0 < prev_value_count
) {
3053 elem
= (user_data_element_t
)prev_values
[0];
3054 assert(0 < elem
->e_made
);
3056 *out_value
= prev_values
[0];
3057 return KERN_SUCCESS
;
3060 /* redeem of default is default */
3062 return KERN_SUCCESS
;
3064 case MACH_VOUCHER_ATTR_USER_DATA_STORE
:
3065 if (USER_DATA_MAX_DATA
< content_size
)
3066 return KERN_RESOURCE_SHORTAGE
;
3068 /* empty is the default */
3069 if (0 == content_size
) {
3071 return KERN_SUCCESS
;
3074 elem
= user_data_dedup(content
, content_size
);
3075 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
3076 return KERN_SUCCESS
;
3079 /* every other command is unknown */
3080 return KERN_INVALID_ARGUMENT
;
3084 static kern_return_t
3085 user_data_extract_content(
3086 ipc_voucher_attr_manager_t __assert_only manager
,
3087 mach_voucher_attr_key_t __assert_only key
,
3088 mach_voucher_attr_value_handle_array_t values
,
3089 mach_voucher_attr_value_handle_array_size_t value_count
,
3090 mach_voucher_attr_recipe_command_t
*out_command
,
3091 mach_voucher_attr_content_t out_content
,
3092 mach_voucher_attr_content_size_t
*in_out_content_size
)
3094 mach_voucher_attr_content_size_t size
= 0;
3095 user_data_element_t elem
;
3098 assert (&user_data_manager
== manager
);
3099 USER_DATA_ASSERT_KEY(key
);
3101 /* concatenate the stored data items */
3102 for (i
= 0; i
< value_count
; i
++) {
3103 elem
= (user_data_element_t
)values
[i
];
3104 assert(USER_DATA_MAX_DATA
>= elem
->e_size
);
3106 if (size
+ elem
->e_size
> *in_out_content_size
)
3107 return KERN_NO_SPACE
;
3109 memcpy(&out_content
[size
], elem
->e_data
, elem
->e_size
);
3110 size
+= elem
->e_size
;
3112 *out_command
= MACH_VOUCHER_ATTR_BITS_STORE
;
3113 *in_out_content_size
= size
;
3114 return KERN_SUCCESS
;
3117 static kern_return_t
3119 ipc_voucher_attr_manager_t __assert_only manager
,
3120 mach_voucher_attr_key_t __assert_only key
,
3121 mach_voucher_attr_value_handle_array_t __unused values
,
3122 mach_msg_type_number_t __unused value_count
,
3123 mach_voucher_attr_command_t __unused command
,
3124 mach_voucher_attr_content_t __unused in_content
,
3125 mach_voucher_attr_content_size_t __unused in_content_size
,
3126 mach_voucher_attr_content_t __unused out_content
,
3127 mach_voucher_attr_content_size_t __unused
*out_content_size
)
3129 assert (&user_data_manager
== manager
);
3130 USER_DATA_ASSERT_KEY(key
);
3131 return KERN_FAILURE
;
3136 ipc_voucher_attr_manager_t manager
)
3138 if (manager
!= &user_data_manager
)
3141 panic("Voucher user-data manager released");
3144 static int user_data_manager_inited
= 0;
3147 user_data_attr_manager_init()
3151 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
3152 if ((user_data_manager_inited
& 0x1) != 0x1) {
3153 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3154 (mach_voucher_attr_value_handle_t
)0,
3155 MACH_VOUCHER_ATTR_KEY_USER_DATA
,
3156 &user_data_control
);
3157 if (KERN_SUCCESS
!= kr
)
3158 printf("Voucher user-data manager register(USER-DATA) returned %d", kr
);
3160 user_data_manager_inited
|= 0x1;
3163 #if defined(MACH_VOUCHER_ATTR_KEY_TEST)
3164 if ((user_data_manager_inited
& 0x2) != 0x2) {
3165 kr
= ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager
,
3166 (mach_voucher_attr_value_handle_t
)0,
3167 MACH_VOUCHER_ATTR_KEY_TEST
,
3169 if (KERN_SUCCESS
!= kr
)
3170 printf("Voucher user-data manager register(TEST) returned %d", kr
);
3172 user_data_manager_inited
|= 0x2;
3175 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
3178 for (i
=0; i
< USER_DATA_HASH_BUCKETS
; i
++)
3179 queue_init(&user_data_bucket
[i
]);
3181 user_data_lock_init();
3185 #endif /* MACH_DEBUG */