xref: /xnu-8019.80.24/osfmk/ipc/ipc_voucher.c (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1 /*
2  * Copyright (c) 2013-2020 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
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.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
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.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 
29 #include <mach/mach_types.h>
30 #include <mach/mach_traps.h>
31 #include <mach/notify.h>
32 #include <ipc/ipc_types.h>
33 #include <ipc/ipc_port.h>
34 #include <ipc/ipc_voucher.h>
35 #include <kern/ipc_kobject.h>
36 #include <kern/ipc_tt.h>
37 #include <kern/mach_param.h>
38 #include <kern/kalloc.h>
39 #include <kern/zalloc.h>
40 
41 #include <libkern/OSAtomic.h>
42 
43 #include <mach/mach_voucher_server.h>
44 #include <mach/mach_voucher_attr_control_server.h>
45 #include <mach/mach_host_server.h>
46 #include <voucher/ipc_pthread_priority_types.h>
47 
48 /*
49  * Sysctl variable; enable and disable tracing of voucher contents
50  */
51 uint32_t ipc_voucher_trace_contents = 0;
52 
53 static SECURITY_READ_ONLY_LATE(zone_t) ipc_voucher_zone;
54 static ZONE_DECLARE(ipc_voucher_attr_control_zone, "ipc voucher attr controls",
55     sizeof(struct ipc_voucher_attr_control), ZC_ZFREE_CLEARMEM);
56 
57 ZONE_INIT(&ipc_voucher_zone, "ipc vouchers", sizeof(struct ipc_voucher),
58     ZC_ZFREE_CLEARMEM, ZONE_ID_IPC_VOUCHERS, NULL);
59 
60 /* deliver voucher notifications */
61 static void ipc_voucher_no_senders(ipc_port_t, mach_port_mscount_t);
62 static void ipc_voucher_attr_control_no_senders(ipc_port_t, mach_port_mscount_t);
63 
64 IPC_KOBJECT_DEFINE(IKOT_VOUCHER,
65     .iko_op_stable     = true,
66     .iko_op_no_senders = ipc_voucher_no_senders);
67 IPC_KOBJECT_DEFINE(IKOT_VOUCHER_ATTR_CONTROL,
68     .iko_op_stable     = true,
69     .iko_op_no_senders = ipc_voucher_attr_control_no_senders);
70 
71 #define voucher_require(v) \
72 	zone_id_require(ZONE_ID_IPC_VOUCHERS, sizeof(struct ipc_voucher), v)
73 
74 /*
75  * Voucher hash table
76  */
77 #define IV_HASH_BUCKETS 127
78 #define IV_HASH_BUCKET(x) ((x) % IV_HASH_BUCKETS)
79 
80 static queue_head_t ivht_bucket[IV_HASH_BUCKETS];
81 static LCK_SPIN_DECLARE_ATTR(ivht_lock_data, &ipc_lck_grp, &ipc_lck_attr);
82 static uint32_t ivht_count = 0;
83 
84 #define ivht_lock_destroy() \
85 	lck_spin_destroy(&ivht_lock_data, &ipc_lck_grp)
86 #define ivht_lock() \
87 	lck_spin_lock_grp(&ivht_lock_data, &ipc_lck_grp)
88 #define ivht_lock_try() \
89 	lck_spin_try_lock_grp(&ivht_lock_data, &ipc_lck_grp)
90 #define ivht_unlock() \
91 	lck_spin_unlock(&ivht_lock_data)
92 
93 /*
94  * Global table of resource manager registrations
95  *
96  * NOTE: For now, limited to well-known resource managers
97  * eventually, will include dynamic allocations requiring
98  * table growth and hashing by key.
99  */
100 static iv_index_t ivgt_keys_in_use = MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN;
101 static ipc_voucher_global_table_element iv_global_table[MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN];
102 static LCK_SPIN_DECLARE_ATTR(ivgt_lock_data, &ipc_lck_grp, &ipc_lck_attr);
103 
104 #define ivgt_lock_destroy() \
105 	lck_spin_destroy(&ivgt_lock_data, &ipc_lck_grp)
106 #define ivgt_lock() \
107 	lck_spin_lock_grp(&ivgt_lock_data, &ipc_lck_grp)
108 #define ivgt_lock_try() \
109 	lck_spin_try_lock_grp(&ivgt_lock_data, &ipc_lck_grp)
110 #define ivgt_unlock() \
111 	lck_spin_unlock(&ivgt_lock_data)
112 
113 ipc_voucher_t iv_alloc(iv_index_t entries);
114 void iv_dealloc(ipc_voucher_t iv, boolean_t unhash);
115 
116 os_refgrp_decl(static, iv_refgrp, "voucher", NULL);
117 os_refgrp_decl(static, ivac_refgrp, "voucher attribute control", NULL);
118 
119 static inline void
iv_reference(ipc_voucher_t iv)120 iv_reference(ipc_voucher_t iv)
121 {
122 	os_ref_retain(&iv->iv_refs);
123 }
124 
125 static inline void
iv_release(ipc_voucher_t iv)126 iv_release(ipc_voucher_t iv)
127 {
128 	if (os_ref_release(&iv->iv_refs) == 0) {
129 		iv_dealloc(iv, TRUE);
130 	}
131 }
132 
133 /*
134  * freelist helper macros
135  */
136 #define IV_FREELIST_END ((iv_index_t) 0)
137 
138 /*
139  * Attribute value hashing helper macros
140  */
141 #define IV_HASH_END UINT32_MAX
142 #define IV_HASH_VAL(sz, val) \
143 	(((val) >> 3) % (sz))
144 
145 static inline iv_index_t
iv_hash_value(iv_index_t key_index,mach_voucher_attr_value_handle_t value)146 iv_hash_value(
147 	iv_index_t key_index,
148 	mach_voucher_attr_value_handle_t value)
149 {
150 	ipc_voucher_attr_control_t ivac;
151 
152 	ivac = iv_global_table[key_index].ivgte_control;
153 	assert(IVAC_NULL != ivac);
154 	return IV_HASH_VAL(ivac->ivac_init_table_size, value);
155 }
156 
157 /*
158  * Convert a key to an index.  This key-index is used to both index
159  * into the voucher table of attribute cache indexes and also the
160  * table of resource managers by key.
161  *
162  * For now, well-known keys have a one-to-one mapping of indexes
163  * into these tables.  But as time goes on, that may not always
164  * be the case (sparse use over time).  This isolates the code from
165  * having to change in these cases - yet still lets us keep a densely
166  * packed set of tables.
167  */
168 static inline iv_index_t
iv_key_to_index(mach_voucher_attr_key_t key)169 iv_key_to_index(mach_voucher_attr_key_t key)
170 {
171 	if (MACH_VOUCHER_ATTR_KEY_ALL == key ||
172 	    MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN < key) {
173 		return IV_UNUSED_KEYINDEX;
174 	}
175 	return (iv_index_t)key - 1;
176 }
177 
178 static inline mach_voucher_attr_key_t
iv_index_to_key(iv_index_t key_index)179 iv_index_to_key(iv_index_t key_index)
180 {
181 	if (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN > key_index) {
182 		return iv_global_table[key_index].ivgte_key;
183 	}
184 	return MACH_VOUCHER_ATTR_KEY_NONE;
185 }
186 
187 static void ivace_release(iv_index_t key_index, iv_index_t value_index);
188 static void ivace_lookup_values(iv_index_t key_index, iv_index_t value_index,
189     mach_voucher_attr_value_handle_array_t  values,
190     mach_voucher_attr_value_handle_array_size_t *count);
191 static ivac_entry_t ivace_lookup(ipc_voucher_attr_control_t ivac,
192     iv_index_t index);
193 
194 static iv_index_t iv_lookup(ipc_voucher_t, iv_index_t);
195 
196 
197 static void ivgt_lookup(iv_index_t,
198     boolean_t,
199     ipc_voucher_attr_manager_t *,
200     ipc_voucher_attr_control_t *);
201 
202 static kern_return_t
203 ipc_voucher_prepare_processing_recipe(
204 	ipc_voucher_t voucher,
205 	ipc_voucher_attr_raw_recipe_array_t recipes,
206 	ipc_voucher_attr_raw_recipe_array_size_t *in_out_size,
207 	mach_voucher_attr_recipe_command_t command,
208 	ipc_voucher_attr_manager_flags flags,
209 	int *need_processing);
210 
211 __startup_func
212 static void
ipc_voucher_init(void)213 ipc_voucher_init(void)
214 {
215 	/* initialize voucher hash */
216 	for (iv_index_t i = 0; i < IV_HASH_BUCKETS; i++) {
217 		queue_init(&ivht_bucket[i]);
218 	}
219 }
220 STARTUP(MACH_IPC, STARTUP_RANK_FIRST, ipc_voucher_init);
221 
222 ipc_voucher_t
iv_alloc(iv_index_t entries)223 iv_alloc(iv_index_t entries)
224 {
225 	ipc_voucher_t iv;
226 	iv_index_t i;
227 
228 
229 	iv = (ipc_voucher_t)zalloc(ipc_voucher_zone);
230 	if (IV_NULL == iv) {
231 		return IV_NULL;
232 	}
233 
234 	os_ref_init(&iv->iv_refs, &iv_refgrp);
235 	iv->iv_sum = 0;
236 	iv->iv_hash = 0;
237 	iv->iv_port = IP_NULL;
238 
239 	if (entries > IV_ENTRIES_INLINE) {
240 		iv_entry_t table;
241 
242 		/* TODO - switch to ipc_table method of allocation */
243 		table = (iv_entry_t) kalloc_data(sizeof(*table) * entries, Z_WAITOK);
244 		if (IVE_NULL == table) {
245 			zfree(ipc_voucher_zone, iv);
246 			return IV_NULL;
247 		}
248 		iv->iv_table = table;
249 		iv->iv_table_size = entries;
250 	} else {
251 		iv->iv_table = iv->iv_inline_table;
252 		iv->iv_table_size = IV_ENTRIES_INLINE;
253 	}
254 
255 	/* initialize the table entries */
256 	for (i = 0; i < iv->iv_table_size; i++) {
257 		iv->iv_table[i] = IV_UNUSED_VALINDEX;
258 	}
259 
260 	return iv;
261 }
262 
263 /*
264  *	Routine:	iv_set
265  *	Purpose:
266  *		Set the voucher's value index for a given key index.
267  *	Conditions:
268  *		This is only called during voucher creation, as
269  *		they are permanent once references are distributed.
270  */
271 static void
iv_set(ipc_voucher_t iv,iv_index_t key_index,iv_index_t value_index)272 iv_set(ipc_voucher_t iv,
273     iv_index_t key_index,
274     iv_index_t value_index)
275 {
276 	if (key_index >= iv->iv_table_size) {
277 		panic("key_index >= iv->iv_table_size");
278 	}
279 	iv->iv_table[key_index] = value_index;
280 }
281 
282 void
iv_dealloc(ipc_voucher_t iv,boolean_t unhash)283 iv_dealloc(ipc_voucher_t iv, boolean_t unhash)
284 {
285 	ipc_port_t port = iv->iv_port;
286 	natural_t i;
287 
288 	/*
289 	 * Do we have to remove it from the hash?
290 	 */
291 	if (unhash) {
292 		ivht_lock();
293 		assert(os_ref_get_count(&iv->iv_refs) == 0);
294 		assert(IV_HASH_BUCKETS > iv->iv_hash);
295 		queue_remove(&ivht_bucket[iv->iv_hash], iv, ipc_voucher_t, iv_hash_link);
296 		ivht_count--;
297 		ivht_unlock();
298 
299 		KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_VOUCHER_DESTROY) | DBG_FUNC_NONE,
300 		    VM_KERNEL_ADDRPERM((uintptr_t)iv), 0, ivht_count, 0, 0);
301 	} else {
302 		os_ref_count_t cnt __assert_only = os_ref_release(&iv->iv_refs);
303 		assert(cnt == 0);
304 	}
305 
306 	/*
307 	 * if a port was allocated for this voucher,
308 	 * it must not have any remaining send rights,
309 	 * because the port's reference on the voucher
310 	 * is gone.  We can just discard it now.
311 	 */
312 	if (IP_VALID(port)) {
313 		assert(port->ip_srights == 0);
314 		ipc_kobject_dealloc_port(port, 0, IKOT_VOUCHER);
315 	}
316 
317 	/* release the attribute references held by this voucher */
318 	for (i = 0; i < iv->iv_table_size; i++) {
319 		ivace_release(i, iv->iv_table[i]);
320 #if MACH_ASSERT
321 		iv_set(iv, i, ~0);
322 #endif
323 	}
324 
325 	if (iv->iv_table != iv->iv_inline_table) {
326 		kfree_data(iv->iv_table, iv->iv_table_size * sizeof(*iv->iv_table));
327 	}
328 
329 	zfree(ipc_voucher_zone, iv);
330 }
331 
332 /*
333  *	Routine:	iv_lookup
334  *	Purpose:
335  *		Find the voucher's value index for a given key_index
336  *	Conditions:
337  *		Vouchers are permanent, so no locking required to do
338  *		a lookup.
339  */
340 static inline iv_index_t
iv_lookup(ipc_voucher_t iv,iv_index_t key_index)341 iv_lookup(ipc_voucher_t iv, iv_index_t key_index)
342 {
343 	if (key_index < iv->iv_table_size) {
344 		return iv->iv_table[key_index];
345 	}
346 	return IV_UNUSED_VALINDEX;
347 }
348 
349 /*
350  *	Routine:	unsafe_convert_port_to_voucher
351  *	Purpose:
352  *		Unsafe conversion of a port to a voucher.
353  *		Intended only for use by trace and debugging
354  *		code. Consumes nothing, validates very little,
355  *		produces an unreferenced voucher, which you
356  *		MAY NOT use as a voucher, only log as an
357  *		address.
358  *	Conditions:
359  *		Caller has a send-right reference to port.
360  *		Port may or may not be locked.
361  */
362 uintptr_t
unsafe_convert_port_to_voucher(ipc_port_t port)363 unsafe_convert_port_to_voucher(
364 	ipc_port_t      port)
365 {
366 	if (IP_VALID(port)) {
367 		return (uintptr_t)ipc_kobject_get_stable(port, IKOT_VOUCHER);
368 	}
369 	return (uintptr_t)IV_NULL;
370 }
371 
372 static ipc_voucher_t
ip_get_voucher(ipc_port_t port)373 ip_get_voucher(ipc_port_t port)
374 {
375 	ipc_voucher_t voucher = ipc_kobject_get_stable(port, IKOT_VOUCHER);
376 	voucher_require(voucher);
377 	return voucher;
378 }
379 
380 /*
381  *	Routine:	convert_port_to_voucher
382  *	Purpose:
383  *		Convert from a port to a voucher.
384  *		Doesn't consume the port [send-right] ref;
385  *		produces a voucher ref,	which may be null.
386  *	Conditions:
387  *		Caller has a send-right reference to port.
388  *		Port may or may not be locked.
389  */
390 ipc_voucher_t
convert_port_to_voucher(ipc_port_t port)391 convert_port_to_voucher(
392 	ipc_port_t      port)
393 {
394 	if (IP_VALID(port) && ip_kotype(port) == IKOT_VOUCHER) {
395 		/*
396 		 * No need to lock because we have a reference on the
397 		 * port, and if it is a true voucher port, that reference
398 		 * keeps the voucher bound to the port (and active).
399 		 */
400 		ipc_voucher_t voucher = ip_get_voucher(port);
401 		ipc_voucher_reference(voucher);
402 		return voucher;
403 	}
404 	return IV_NULL;
405 }
406 
407 /*
408  *	Routine:	convert_port_name_to_voucher
409  *	Purpose:
410  *		Convert from a port name in the current space to a voucher.
411  *		Produces a voucher ref,	which may be null.
412  *	Conditions:
413  *		Nothing locked.
414  */
415 
416 ipc_voucher_t
convert_port_name_to_voucher(mach_port_name_t voucher_name)417 convert_port_name_to_voucher(
418 	mach_port_name_t        voucher_name)
419 {
420 	ipc_voucher_t iv;
421 	kern_return_t kr;
422 	ipc_port_t port;
423 
424 	if (MACH_PORT_VALID(voucher_name)) {
425 		kr = ipc_port_translate_send(current_space(), voucher_name, &port);
426 		if (KERN_SUCCESS != kr) {
427 			return IV_NULL;
428 		}
429 
430 		iv = convert_port_to_voucher(port);
431 		ip_mq_unlock(port);
432 		return iv;
433 	}
434 	return IV_NULL;
435 }
436 
437 
438 void
ipc_voucher_reference(ipc_voucher_t voucher)439 ipc_voucher_reference(ipc_voucher_t voucher)
440 {
441 	if (IPC_VOUCHER_NULL == voucher) {
442 		return;
443 	}
444 
445 	iv_reference(voucher);
446 }
447 
448 void
ipc_voucher_release(ipc_voucher_t voucher)449 ipc_voucher_release(ipc_voucher_t voucher)
450 {
451 	if (IPC_VOUCHER_NULL != voucher) {
452 		iv_release(voucher);
453 	}
454 }
455 
456 /*
457  * Routine:	ipc_voucher_no_senders
458  * Purpose:
459  *	Called whenever the Mach port system detects no-senders
460  *	on the voucher port.
461  */
462 static void
ipc_voucher_no_senders(ipc_port_t port,__unused mach_port_mscount_t mscount)463 ipc_voucher_no_senders(ipc_port_t port, __unused mach_port_mscount_t mscount)
464 {
465 	ipc_voucher_t voucher = ip_get_voucher(port);
466 
467 	assert(IKOT_VOUCHER == ip_kotype(port));
468 
469 	/* consume the reference donated by convert_voucher_to_port */
470 	ipc_voucher_release(voucher);
471 }
472 
473 /*
474  * Convert a voucher to a port.
475  */
476 ipc_port_t
convert_voucher_to_port(ipc_voucher_t voucher)477 convert_voucher_to_port(ipc_voucher_t voucher)
478 {
479 	if (IV_NULL == voucher) {
480 		return IP_NULL;
481 	}
482 
483 	voucher_require(voucher);
484 	assert(os_ref_get_count(&voucher->iv_refs) > 0);
485 
486 	/*
487 	 * make a send right and donate our reference for ipc_voucher_no_senders
488 	 * if this is the first send right
489 	 */
490 	if (!ipc_kobject_make_send_lazy_alloc_port(&voucher->iv_port,
491 	    (ipc_kobject_t)voucher, IKOT_VOUCHER, IPC_KOBJECT_ALLOC_NONE, 0)) {
492 		ipc_voucher_release(voucher);
493 	}
494 	return voucher->iv_port;
495 }
496 
497 #define ivace_reset_data(ivace_elem, next_index) {       \
498 	(ivace_elem)->ivace_value = 0xDEADC0DEDEADC0DE;  \
499 	(ivace_elem)->ivace_refs = 0;                    \
500 	(ivace_elem)->ivace_persist = 0;                 \
501 	(ivace_elem)->ivace_made = 0;                    \
502 	(ivace_elem)->ivace_free = TRUE;                 \
503 	(ivace_elem)->ivace_releasing = FALSE;           \
504 	(ivace_elem)->ivace_layered = 0;                 \
505 	(ivace_elem)->ivace_index = IV_HASH_END;         \
506 	(ivace_elem)->ivace_next = (next_index);         \
507 }
508 
509 #define ivace_copy_data(ivace_src_elem, ivace_dst_elem) {  \
510 	(ivace_dst_elem)->ivace_value = (ivace_src_elem)->ivace_value; \
511 	(ivace_dst_elem)->ivace_refs = (ivace_src_elem)->ivace_refs;   \
512 	(ivace_dst_elem)->ivace_persist = (ivace_src_elem)->ivace_persist; \
513 	(ivace_dst_elem)->ivace_made = (ivace_src_elem)->ivace_made;   \
514 	(ivace_dst_elem)->ivace_free = (ivace_src_elem)->ivace_free;   \
515 	(ivace_dst_elem)->ivace_layered = (ivace_src_elem)->ivace_layered;   \
516 	(ivace_dst_elem)->ivace_releasing = (ivace_src_elem)->ivace_releasing; \
517 	(ivace_dst_elem)->ivace_index = (ivace_src_elem)->ivace_index; \
518 	(ivace_dst_elem)->ivace_next = (ivace_src_elem)->ivace_next; \
519 }
520 
521 ipc_voucher_attr_control_t
ivac_alloc(iv_index_t key_index)522 ivac_alloc(iv_index_t key_index)
523 {
524 	ipc_voucher_attr_control_t ivac;
525 	ivac_entry_t table;
526 	natural_t i;
527 
528 
529 	ivac = (ipc_voucher_attr_control_t)zalloc(ipc_voucher_attr_control_zone);
530 	if (IVAC_NULL == ivac) {
531 		return IVAC_NULL;
532 	}
533 
534 	os_ref_init(&ivac->ivac_refs, &ivac_refgrp);
535 	ivac->ivac_is_growing = FALSE;
536 	ivac->ivac_port = IP_NULL;
537 
538 	/* start with just the inline table */
539 	table = kalloc_type(struct ivac_entry_s, IVAC_ENTRIES_MIN, Z_WAITOK | Z_ZERO);
540 	ivac->ivac_table = table;
541 	ivac->ivac_table_size = IVAC_ENTRIES_MIN;
542 	ivac->ivac_init_table_size = IVAC_ENTRIES_MIN;
543 	for (i = 0; i < ivac->ivac_table_size; i++) {
544 		ivace_reset_data(&table[i], i + 1);
545 	}
546 
547 	/* the default table entry is never on freelist */
548 	table[0].ivace_next = IV_HASH_END;
549 	table[0].ivace_free = FALSE;
550 	table[i - 1].ivace_next = IV_FREELIST_END;
551 	ivac->ivac_freelist = 1;
552 	ivac_lock_init(ivac);
553 	ivac->ivac_key_index = key_index;
554 	return ivac;
555 }
556 
557 
558 void
ivac_dealloc(ipc_voucher_attr_control_t ivac)559 ivac_dealloc(ipc_voucher_attr_control_t ivac)
560 {
561 	ipc_voucher_attr_manager_t ivam = IVAM_NULL;
562 	iv_index_t key_index = ivac->ivac_key_index;
563 	ipc_port_t port = ivac->ivac_port;
564 	natural_t i;
565 
566 	/*
567 	 * If the control is in the global table, we
568 	 * have to remove it from there before we (re)confirm
569 	 * that the reference count is still zero.
570 	 */
571 	ivgt_lock();
572 	if (os_ref_get_count(&ivac->ivac_refs) > 0) {
573 		ivgt_unlock();
574 		return;
575 	}
576 
577 	/* take it out of the global table */
578 	if (iv_global_table[key_index].ivgte_control == ivac) {
579 		ivam = iv_global_table[key_index].ivgte_manager;
580 		iv_global_table[key_index].ivgte_manager = IVAM_NULL;
581 		iv_global_table[key_index].ivgte_control = IVAC_NULL;
582 		iv_global_table[key_index].ivgte_key = MACH_VOUCHER_ATTR_KEY_NONE;
583 	}
584 	ivgt_unlock();
585 
586 	/* release the reference held on the resource manager */
587 	if (IVAM_NULL != ivam) {
588 		(ivam->ivam_release)(ivam);
589 	}
590 
591 	/*
592 	 * if a port was allocated for this voucher,
593 	 * it must not have any remaining send rights,
594 	 * because the port's reference on the voucher
595 	 * is gone.  We can just discard it now.
596 	 */
597 	if (IP_VALID(port)) {
598 		assert(port->ip_srights == 0);
599 		ipc_kobject_dealloc_port(port, 0, IKOT_VOUCHER_ATTR_CONTROL);
600 	}
601 
602 	/*
603 	 * the resource manager's control reference and all references
604 	 * held by the specific value caches are gone, so free the
605 	 * table.
606 	 */
607 #ifdef MACH_DEBUG
608 	for (i = 0; i < ivac->ivac_table_size; i++) {
609 		if (ivac->ivac_table[i].ivace_refs != 0) {
610 			panic("deallocing a resource manager with live refs to its attr values");
611 		}
612 	}
613 #endif
614 	kfree_type(struct ivac_entry_s, ivac->ivac_init_table_size, ivac->ivac_table);
615 	ivac_lock_destroy(ivac);
616 	zfree(ipc_voucher_attr_control_zone, ivac);
617 }
618 
619 void
ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control)620 ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control)
621 {
622 	ivac_reference(control);
623 }
624 
625 void
ipc_voucher_attr_control_release(ipc_voucher_attr_control_t control)626 ipc_voucher_attr_control_release(ipc_voucher_attr_control_t control)
627 {
628 	ivac_release(control);
629 }
630 
631 /*
632  *	Routine:	convert_port_to_voucher_attr_control reference
633  *	Purpose:
634  *		Convert from a port to a voucher attribute control.
635  *		Doesn't consume the port ref; produces a voucher ref,
636  *		which may be null.
637  *	Conditions:
638  *		Nothing locked.
639  */
640 ipc_voucher_attr_control_t
convert_port_to_voucher_attr_control(ipc_port_t port)641 convert_port_to_voucher_attr_control(
642 	ipc_port_t      port)
643 {
644 	ipc_voucher_attr_control_t ivac = IVAC_NULL;
645 
646 	if (IP_VALID(port)) {
647 		ivac = ipc_kobject_get_stable(port, IKOT_VOUCHER_ATTR_CONTROL);
648 	}
649 	if (ivac != IVAC_NULL) {
650 		zone_require(ipc_voucher_attr_control_zone, ivac);
651 		ivac_reference(ivac);
652 	}
653 	return IVAC_NULL;
654 }
655 
656 /*
657  * Routine:	ipc_voucher_attr_control_no_senders
658  * Purpose:
659  *	Called whenever the Mach port system detects no-senders
660  *	on the voucher attr control port.
661  */
662 static void
ipc_voucher_attr_control_no_senders(ipc_port_t port,__unused mach_port_mscount_t mscount)663 ipc_voucher_attr_control_no_senders(ipc_port_t port, __unused mach_port_mscount_t mscount)
664 {
665 	ipc_voucher_attr_control_t ivac;
666 
667 	ivac = ipc_kobject_get_stable(port, IKOT_VOUCHER_ATTR_CONTROL);
668 	ivac_release(ivac);
669 }
670 
671 /*
672  * Convert a voucher attr control to a port.
673  */
674 ipc_port_t
convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control)675 convert_voucher_attr_control_to_port(ipc_voucher_attr_control_t control)
676 {
677 	if (IVAC_NULL == control) {
678 		return IP_NULL;
679 	}
680 
681 	zone_require(ipc_voucher_attr_control_zone, control);
682 
683 	/*
684 	 * make a send right and donate our reference for
685 	 * ipc_voucher_attr_control_no_senders if this is the first send right
686 	 */
687 	if (!ipc_kobject_make_send_lazy_alloc_port(&control->ivac_port,
688 	    (ipc_kobject_t)control, IKOT_VOUCHER_ATTR_CONTROL,
689 	    IPC_KOBJECT_ALLOC_NONE, 0)) {
690 		ivac_release(control);
691 	}
692 	return control->ivac_port;
693 }
694 
695 /*
696  * Look up the values for a given <key, index> pair.
697  */
698 static void
ivace_lookup_values(iv_index_t key_index,iv_index_t value_index,mach_voucher_attr_value_handle_array_t values,mach_voucher_attr_value_handle_array_size_t * count)699 ivace_lookup_values(
700 	iv_index_t                              key_index,
701 	iv_index_t                              value_index,
702 	mach_voucher_attr_value_handle_array_t          values,
703 	mach_voucher_attr_value_handle_array_size_t     *count)
704 {
705 	ipc_voucher_attr_control_t ivac;
706 	ivac_entry_t ivace;
707 
708 	if (IV_UNUSED_VALINDEX == value_index ||
709 	    MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN <= key_index) {
710 		*count = 0;
711 		return;
712 	}
713 
714 	ivac = iv_global_table[key_index].ivgte_control;
715 	assert(IVAC_NULL != ivac);
716 
717 	/*
718 	 * Get the entry and then the linked values.
719 	 */
720 	ivac_lock(ivac);
721 	ivace = ivace_lookup(ivac, value_index);
722 
723 	/*
724 	 * TODO: support chained values (for effective vouchers).
725 	 */
726 	assert(ivace->ivace_refs > 0);
727 	values[0] = ivace->ivace_value;
728 	ivac_unlock(ivac);
729 	*count = 1;
730 }
731 
732 /*
733  * Lookup the entry at the given index into the table
734  */
735 static inline ivac_entry_t
ivace_lookup(ipc_voucher_attr_control_t ivac,iv_index_t index)736 ivace_lookup(ipc_voucher_attr_control_t ivac, iv_index_t index)
737 {
738 	if (index >= ivac->ivac_table_size) {
739 		panic("index >= ivac->ivac_table_size");
740 	}
741 	return &ivac->ivac_table[index];
742 }
743 
744 /*
745  *  ivac_grow_table - Allocate a bigger table of attribute values
746  *
747  *  Conditions:	ivac is locked on entry and again on return
748  */
749 static void
ivac_grow_table(ipc_voucher_attr_control_t ivac)750 ivac_grow_table(ipc_voucher_attr_control_t ivac)
751 {
752 	iv_index_t i = 0;
753 
754 	/* NOTE: do not modify *_table and *_size values once set */
755 	ivac_entry_t new_table = NULL, old_table = NULL;
756 	iv_index_t new_size, old_size;
757 
758 	if (ivac->ivac_is_growing) {
759 		ivac_sleep(ivac);
760 		return;
761 	}
762 
763 	ivac->ivac_is_growing = 1;
764 	if (ivac->ivac_table_size >= IVAC_ENTRIES_MAX) {
765 		panic("Cannot grow ipc space beyond IVAC_ENTRIES_MAX. Some process is leaking vouchers");
766 		return;
767 	}
768 
769 	old_size = ivac->ivac_table_size;
770 	ivac_unlock(ivac);
771 
772 	new_size = old_size * 2;
773 
774 	assert(new_size > old_size);
775 	assert(new_size < IVAC_ENTRIES_MAX);
776 
777 	new_table = kalloc_type(struct ivac_entry_s, new_size, Z_WAITOK | Z_ZERO);
778 	if (!new_table) {
779 		panic("Failed to grow ivac table to size %d", new_size);
780 		return;
781 	}
782 
783 	/* setup the free list for new entries */
784 	for (i = old_size; i < new_size; i++) {
785 		ivace_reset_data(&new_table[i], i + 1);
786 	}
787 
788 	ivac_lock(ivac);
789 
790 	for (i = 0; i < ivac->ivac_table_size; i++) {
791 		ivace_copy_data(&ivac->ivac_table[i], &new_table[i]);
792 	}
793 
794 	old_table = ivac->ivac_table;
795 
796 	ivac->ivac_table = new_table;
797 	ivac->ivac_table_size = new_size;
798 
799 	/* adding new free entries at head of freelist */
800 	ivac->ivac_table[new_size - 1].ivace_next = ivac->ivac_freelist;
801 	ivac->ivac_freelist = old_size;
802 	ivac->ivac_is_growing = 0;
803 	ivac_wakeup(ivac);
804 
805 	if (old_table) {
806 		ivac_unlock(ivac);
807 		kfree_type(struct ivac_entry_s, old_size, old_table);
808 		ivac_lock(ivac);
809 	}
810 }
811 
812 /*
813  * ivace_reference_by_index
814  *
815  * Take an additional reference on the <key_index, val_index>
816  * cached value. It is assumed the caller already holds a
817  * reference to the same cached key-value pair.
818  */
819 static void
ivace_reference_by_index(iv_index_t key_index,iv_index_t val_index)820 ivace_reference_by_index(
821 	iv_index_t      key_index,
822 	iv_index_t      val_index)
823 {
824 	ipc_voucher_attr_control_t ivac;
825 	ivac_entry_t ivace;
826 
827 	if (IV_UNUSED_VALINDEX == val_index) {
828 		return;
829 	}
830 
831 	ivgt_lookup(key_index, FALSE, NULL, &ivac);
832 	assert(IVAC_NULL != ivac);
833 
834 	ivac_lock(ivac);
835 	ivace = ivace_lookup(ivac, val_index);
836 
837 	assert(0xdeadc0dedeadc0de != ivace->ivace_value);
838 	assert(0 < ivace->ivace_refs);
839 	assert(!ivace->ivace_free);
840 
841 	/* Take ref only on non-persistent values */
842 	if (!ivace->ivace_persist) {
843 		ivace->ivace_refs++;
844 	}
845 	ivac_unlock(ivac);
846 }
847 
848 
849 /*
850  * Look up the values for a given <key, index> pair.
851  *
852  * Consumes a reference on the passed voucher control.
853  * Either it is donated to a newly-created value cache
854  * or it is released (if we piggy back on an existing
855  * value cache entry).
856  */
857 static iv_index_t
ivace_reference_by_value(ipc_voucher_attr_control_t ivac,mach_voucher_attr_value_handle_t value,mach_voucher_attr_value_flags_t flag)858 ivace_reference_by_value(
859 	ipc_voucher_attr_control_t      ivac,
860 	mach_voucher_attr_value_handle_t        value,
861 	mach_voucher_attr_value_flags_t          flag)
862 {
863 	ivac_entry_t ivace = IVACE_NULL;
864 	iv_index_t hash_index;
865 	iv_index_t *index_p;
866 	iv_index_t index;
867 
868 	if (IVAC_NULL == ivac) {
869 		return IV_UNUSED_VALINDEX;
870 	}
871 
872 	ivac_lock(ivac);
873 restart:
874 	hash_index = IV_HASH_VAL(ivac->ivac_init_table_size, value);
875 	index_p = &ivace_lookup(ivac, hash_index)->ivace_index;
876 	index = *index_p;
877 	while (index != IV_HASH_END) {
878 		ivace = ivace_lookup(ivac, index);
879 		assert(!ivace->ivace_free);
880 
881 		if (ivace->ivace_value == value) {
882 			break;
883 		}
884 
885 		assert(ivace->ivace_next != index);
886 		index = ivace->ivace_next;
887 	}
888 
889 	/* found it? */
890 	if (index != IV_HASH_END) {
891 		/* only add reference on non-persistent value */
892 		if (!ivace->ivace_persist) {
893 			ivace->ivace_refs++;
894 			ivace->ivace_made++;
895 		}
896 
897 		ivac_unlock(ivac);
898 		ivac_release(ivac);
899 		return index;
900 	}
901 
902 	/* insert new entry in the table */
903 	index = ivac->ivac_freelist;
904 	if (IV_FREELIST_END == index) {
905 		/* freelist empty */
906 		ivac_grow_table(ivac);
907 		goto restart;
908 	}
909 
910 	/* take the entry off the freelist */
911 	ivace = ivace_lookup(ivac, index);
912 	ivac->ivac_freelist = ivace->ivace_next;
913 
914 	/* initialize the new entry */
915 	ivace->ivace_value = value;
916 	ivace->ivace_refs = 1;
917 	ivace->ivace_made = 1;
918 	ivace->ivace_free = FALSE;
919 	ivace->ivace_persist = (flag & MACH_VOUCHER_ATTR_VALUE_FLAGS_PERSIST) ? TRUE : FALSE;
920 
921 	/* insert the new entry in the proper hash chain */
922 	ivace->ivace_next = *index_p;
923 	*index_p = index;
924 	ivac_unlock(ivac);
925 
926 	/* donated passed in ivac reference to new entry */
927 
928 	return index;
929 }
930 
931 /*
932  * Release a reference on the given <key_index, value_index> pair.
933  *
934  * Conditions:	called with nothing locked, as it may cause
935  *		callouts and/or messaging to the resource
936  *		manager.
937  */
938 static void
ivace_release(iv_index_t key_index,iv_index_t value_index)939 ivace_release(
940 	iv_index_t key_index,
941 	iv_index_t value_index)
942 {
943 	ipc_voucher_attr_control_t ivac;
944 	ipc_voucher_attr_manager_t ivam;
945 	mach_voucher_attr_value_handle_t value;
946 	mach_voucher_attr_value_reference_t made;
947 	mach_voucher_attr_key_t key;
948 	iv_index_t hash_index;
949 	ivac_entry_t ivace;
950 	ivac_entry_t ivace_tmp;
951 	kern_return_t kr;
952 
953 	/* cant release the default value */
954 	if (IV_UNUSED_VALINDEX == value_index) {
955 		return;
956 	}
957 
958 	ivgt_lookup(key_index, FALSE, &ivam, &ivac);
959 	assert(IVAC_NULL != ivac);
960 	assert(IVAM_NULL != ivam);
961 
962 	ivac_lock(ivac);
963 	ivace = ivace_lookup(ivac, value_index);
964 
965 	assert(0 < ivace->ivace_refs);
966 
967 	/* cant release persistent values */
968 	if (ivace->ivace_persist) {
969 		ivac_unlock(ivac);
970 		return;
971 	}
972 
973 	if (0 < --ivace->ivace_refs) {
974 		ivac_unlock(ivac);
975 		return;
976 	}
977 
978 	key = iv_index_to_key(key_index);
979 	assert(MACH_VOUCHER_ATTR_KEY_NONE != key);
980 
981 	/*
982 	 * if last return reply is still pending,
983 	 * let it handle this later return when
984 	 * the previous reply comes in.
985 	 */
986 	if (ivace->ivace_releasing) {
987 		ivac_unlock(ivac);
988 		return;
989 	}
990 
991 	/* claim releasing */
992 	ivace->ivace_releasing = TRUE;
993 	value = ivace->ivace_value;
994 
995 redrive:
996 	assert(value == ivace->ivace_value);
997 	assert(!ivace->ivace_free);
998 	made = ivace->ivace_made;
999 	ivac_unlock(ivac);
1000 
1001 	/* callout to manager's release_value */
1002 	kr = (ivam->ivam_release_value)(ivam, key, value, made);
1003 
1004 	/* recalculate entry address as table may have changed */
1005 	ivac_lock(ivac);
1006 	ivace = ivace_lookup(ivac, value_index);
1007 	assert(value == ivace->ivace_value);
1008 
1009 	/*
1010 	 * new made values raced with this return.  If the
1011 	 * manager OK'ed the prior release, we have to start
1012 	 * the made numbering over again (pretend the race
1013 	 * didn't happen). If the entry has zero refs again,
1014 	 * re-drive the release.
1015 	 */
1016 	if (ivace->ivace_made != made) {
1017 		if (KERN_SUCCESS == kr) {
1018 			ivace->ivace_made -= made;
1019 		}
1020 
1021 		if (0 == ivace->ivace_refs) {
1022 			goto redrive;
1023 		}
1024 
1025 		ivace->ivace_releasing = FALSE;
1026 		ivac_unlock(ivac);
1027 		return;
1028 	} else {
1029 		/*
1030 		 * If the manager returned FAILURE, someone took a
1031 		 * reference on the value but have not updated the ivace,
1032 		 * release the lock and return since thread who got
1033 		 * the new reference will update the ivace and will have
1034 		 * non-zero reference on the value.
1035 		 */
1036 		if (KERN_SUCCESS != kr) {
1037 			ivace->ivace_releasing = FALSE;
1038 			ivac_unlock(ivac);
1039 			return;
1040 		}
1041 	}
1042 
1043 	assert(0 == ivace->ivace_refs);
1044 
1045 	/*
1046 	 * going away - remove entry from its hash
1047 	 * If its at the head of the hash bucket list (common), unchain
1048 	 * at the head. Otherwise walk the chain until the next points
1049 	 * at this entry, and remove it from the the list there.
1050 	 */
1051 	hash_index = iv_hash_value(key_index, value);
1052 	ivace_tmp = ivace_lookup(ivac, hash_index);
1053 	if (ivace_tmp->ivace_index == value_index) {
1054 		ivace_tmp->ivace_index = ivace->ivace_next;
1055 	} else {
1056 		hash_index = ivace_tmp->ivace_index;
1057 		ivace_tmp = ivace_lookup(ivac, hash_index);
1058 		assert(IV_HASH_END != hash_index);
1059 		while (ivace_tmp->ivace_next != value_index) {
1060 			hash_index = ivace_tmp->ivace_next;
1061 			assert(IV_HASH_END != hash_index);
1062 			ivace_tmp = ivace_lookup(ivac, hash_index);
1063 		}
1064 		ivace_tmp->ivace_next = ivace->ivace_next;
1065 	}
1066 
1067 	/* Put this entry on the freelist */
1068 	ivace->ivace_value = 0xdeadc0dedeadc0de;
1069 	ivace->ivace_releasing = FALSE;
1070 	ivace->ivace_free = TRUE;
1071 	ivace->ivace_made = 0;
1072 	ivace->ivace_next = ivac->ivac_freelist;
1073 	ivac->ivac_freelist = value_index;
1074 	ivac_unlock(ivac);
1075 
1076 	/* release the reference this value held on its cache control */
1077 	ivac_release(ivac);
1078 
1079 	return;
1080 }
1081 
1082 
1083 /*
1084  * ivgt_looup
1085  *
1086  * Lookup an entry in the global table from the context of a manager
1087  * registration.  Adds a reference to the control to keep the results
1088  * around (if needed).
1089  *
1090  * Because of the calling point, we can't be sure the manager is
1091  * [fully] registered yet.  So, we must hold the global table lock
1092  * during the lookup to synchronize with in-parallel registrations
1093  * (and possible table growth).
1094  */
1095 static void
ivgt_lookup(iv_index_t key_index,boolean_t take_reference,ipc_voucher_attr_manager_t * manager,ipc_voucher_attr_control_t * control)1096 ivgt_lookup(iv_index_t key_index,
1097     boolean_t take_reference,
1098     ipc_voucher_attr_manager_t *manager,
1099     ipc_voucher_attr_control_t *control)
1100 {
1101 	ipc_voucher_attr_control_t ivac;
1102 
1103 	if (key_index < MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN) {
1104 		ivgt_lock();
1105 		if (NULL != manager) {
1106 			*manager = iv_global_table[key_index].ivgte_manager;
1107 		}
1108 		ivac = iv_global_table[key_index].ivgte_control;
1109 		if (IVAC_NULL != ivac) {
1110 			assert(key_index == ivac->ivac_key_index);
1111 			if (take_reference) {
1112 				assert(NULL != control);
1113 				ivac_reference(ivac);
1114 			}
1115 		}
1116 		ivgt_unlock();
1117 		if (NULL != control) {
1118 			*control = ivac;
1119 		}
1120 	} else {
1121 		if (NULL != manager) {
1122 			*manager = IVAM_NULL;
1123 		}
1124 		if (NULL != control) {
1125 			*control = IVAC_NULL;
1126 		}
1127 	}
1128 }
1129 
1130 /*
1131  *	Routine:	ipc_replace_voucher_value
1132  *	Purpose:
1133  *		Replace the <voucher, key> value with the results of
1134  *		running the supplied command through the resource
1135  *		manager's get-value callback.
1136  *	Conditions:
1137  *		Nothing locked (may invoke user-space repeatedly).
1138  *		Caller holds references on voucher and previous voucher.
1139  */
1140 static kern_return_t
ipc_replace_voucher_value(ipc_voucher_t voucher,mach_voucher_attr_key_t key,mach_voucher_attr_recipe_command_t command,ipc_voucher_t prev_voucher,mach_voucher_attr_content_t content,mach_voucher_attr_content_size_t content_size)1141 ipc_replace_voucher_value(
1142 	ipc_voucher_t                           voucher,
1143 	mach_voucher_attr_key_t                 key,
1144 	mach_voucher_attr_recipe_command_t      command,
1145 	ipc_voucher_t                           prev_voucher,
1146 	mach_voucher_attr_content_t             content,
1147 	mach_voucher_attr_content_size_t        content_size)
1148 {
1149 	mach_voucher_attr_value_handle_t previous_vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
1150 	mach_voucher_attr_value_handle_array_size_t previous_vals_count;
1151 	mach_voucher_attr_value_handle_t new_value;
1152 	mach_voucher_attr_value_flags_t new_flag;
1153 	ipc_voucher_t new_value_voucher;
1154 	ipc_voucher_attr_manager_t ivam;
1155 	ipc_voucher_attr_control_t ivac;
1156 	iv_index_t prev_val_index;
1157 	iv_index_t save_val_index;
1158 	iv_index_t val_index;
1159 	iv_index_t key_index;
1160 	kern_return_t kr;
1161 
1162 	/*
1163 	 * Get the manager for this key_index.
1164 	 * Returns a reference on the control.
1165 	 */
1166 	key_index = iv_key_to_index(key);
1167 	ivgt_lookup(key_index, TRUE, &ivam, &ivac);
1168 	if (IVAM_NULL == ivam) {
1169 		return KERN_INVALID_ARGUMENT;
1170 	}
1171 
1172 	/* save the current value stored in the forming voucher */
1173 	save_val_index = iv_lookup(voucher, key_index);
1174 
1175 	/*
1176 	 * Get the previous value(s) for this key creation.
1177 	 * If a previous voucher is specified, they come from there.
1178 	 * Otherwise, they come from the intermediate values already
1179 	 * in the forming voucher.
1180 	 */
1181 	prev_val_index = (IV_NULL != prev_voucher) ?
1182 	    iv_lookup(prev_voucher, key_index) :
1183 	    save_val_index;
1184 	ivace_lookup_values(key_index, prev_val_index,
1185 	    previous_vals, &previous_vals_count);
1186 
1187 	/* Call out to resource manager to get new value */
1188 	new_value_voucher = IV_NULL;
1189 	kr = (ivam->ivam_get_value)(
1190 		ivam, key, command,
1191 		previous_vals, previous_vals_count,
1192 		content, content_size,
1193 		&new_value, &new_flag, &new_value_voucher);
1194 	if (KERN_SUCCESS != kr) {
1195 		ivac_release(ivac);
1196 		return kr;
1197 	}
1198 
1199 	/* TODO: value insertion from returned voucher */
1200 	if (IV_NULL != new_value_voucher) {
1201 		iv_release(new_value_voucher);
1202 	}
1203 
1204 	/*
1205 	 * Find or create a slot in the table associated
1206 	 * with this attribute value.  The ivac reference
1207 	 * is transferred to a new value, or consumed if
1208 	 * we find a matching existing value.
1209 	 */
1210 	val_index = ivace_reference_by_value(ivac, new_value, new_flag);
1211 	iv_set(voucher, key_index, val_index);
1212 
1213 	/*
1214 	 * release saved old value from the newly forming voucher
1215 	 * This is saved until the end to avoid churning the
1216 	 * release logic in cases where the same value is returned
1217 	 * as was there before.
1218 	 */
1219 	ivace_release(key_index, save_val_index);
1220 
1221 	return KERN_SUCCESS;
1222 }
1223 
1224 /*
1225  *	Routine:	ipc_directly_replace_voucher_value
1226  *	Purpose:
1227  *		Replace the <voucher, key> value with the value-handle
1228  *		supplied directly by the attribute manager.
1229  *	Conditions:
1230  *		Nothing locked.
1231  *		Caller holds references on voucher.
1232  *		A made reference to the value-handle is donated by the caller.
1233  */
1234 static kern_return_t
ipc_directly_replace_voucher_value(ipc_voucher_t voucher,mach_voucher_attr_key_t key,mach_voucher_attr_value_handle_t new_value)1235 ipc_directly_replace_voucher_value(
1236 	ipc_voucher_t                           voucher,
1237 	mach_voucher_attr_key_t                 key,
1238 	mach_voucher_attr_value_handle_t        new_value)
1239 {
1240 	ipc_voucher_attr_manager_t ivam;
1241 	ipc_voucher_attr_control_t ivac;
1242 	iv_index_t save_val_index;
1243 	iv_index_t val_index;
1244 	iv_index_t key_index;
1245 
1246 	/*
1247 	 * Get the manager for this key_index.
1248 	 * Returns a reference on the control.
1249 	 */
1250 	key_index = iv_key_to_index(key);
1251 	ivgt_lookup(key_index, TRUE, &ivam, &ivac);
1252 	if (IVAM_NULL == ivam) {
1253 		return KERN_INVALID_ARGUMENT;
1254 	}
1255 
1256 	/* save the current value stored in the forming voucher */
1257 	save_val_index = iv_lookup(voucher, key_index);
1258 
1259 	/*
1260 	 * Find or create a slot in the table associated
1261 	 * with this attribute value.  The ivac reference
1262 	 * is transferred to a new value, or consumed if
1263 	 * we find a matching existing value.
1264 	 */
1265 	val_index = ivace_reference_by_value(ivac, new_value,
1266 	    MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE);
1267 	iv_set(voucher, key_index, val_index);
1268 
1269 	/*
1270 	 * release saved old value from the newly forming voucher
1271 	 * This is saved until the end to avoid churning the
1272 	 * release logic in cases where the same value is returned
1273 	 * as was there before.
1274 	 */
1275 	ivace_release(key_index, save_val_index);
1276 
1277 	return KERN_SUCCESS;
1278 }
1279 
1280 static kern_return_t
ipc_execute_voucher_recipe_command(ipc_voucher_t voucher,mach_voucher_attr_key_t key,mach_voucher_attr_recipe_command_t command,ipc_voucher_t prev_iv,mach_voucher_attr_content_t content,mach_voucher_attr_content_size_t content_size,boolean_t key_priv)1281 ipc_execute_voucher_recipe_command(
1282 	ipc_voucher_t                           voucher,
1283 	mach_voucher_attr_key_t                 key,
1284 	mach_voucher_attr_recipe_command_t      command,
1285 	ipc_voucher_t                           prev_iv,
1286 	mach_voucher_attr_content_t             content,
1287 	mach_voucher_attr_content_size_t        content_size,
1288 	boolean_t                               key_priv)
1289 {
1290 	iv_index_t prev_val_index;
1291 	iv_index_t val_index;
1292 	kern_return_t kr;
1293 
1294 	switch (command) {
1295 	/*
1296 	 * MACH_VOUCHER_ATTR_COPY
1297 	 *	Copy the attribute(s) from the previous voucher to the new
1298 	 *	one.  A wildcard key is an acceptable value - indicating a
1299 	 *	desire to copy all the attribute values from the previous
1300 	 *	voucher.
1301 	 */
1302 	case MACH_VOUCHER_ATTR_COPY:
1303 
1304 		/* no recipe data on a copy */
1305 		if (0 < content_size) {
1306 			return KERN_INVALID_ARGUMENT;
1307 		}
1308 
1309 		/* nothing to copy from? - done */
1310 		if (IV_NULL == prev_iv) {
1311 			return KERN_SUCCESS;
1312 		}
1313 
1314 		if (MACH_VOUCHER_ATTR_KEY_ALL == key) {
1315 			iv_index_t limit, j;
1316 
1317 			/* reconcile possible difference in voucher sizes */
1318 			limit = (prev_iv->iv_table_size < voucher->iv_table_size) ?
1319 			    prev_iv->iv_table_size :
1320 			    voucher->iv_table_size;
1321 
1322 			/* wildcard matching */
1323 			for (j = 0; j < limit; j++) {
1324 				/* release old value being replaced */
1325 				val_index = iv_lookup(voucher, j);
1326 				ivace_release(j, val_index);
1327 
1328 				/* replace with reference to prev voucher's value */
1329 				prev_val_index = iv_lookup(prev_iv, j);
1330 				ivace_reference_by_index(j, prev_val_index);
1331 				iv_set(voucher, j, prev_val_index);
1332 			}
1333 		} else {
1334 			iv_index_t key_index;
1335 
1336 			/* copy just one key */
1337 			key_index = iv_key_to_index(key);
1338 			if (ivgt_keys_in_use < key_index) {
1339 				return KERN_INVALID_ARGUMENT;
1340 			}
1341 
1342 			/* release old value being replaced */
1343 			val_index = iv_lookup(voucher, key_index);
1344 			ivace_release(key_index, val_index);
1345 
1346 			/* replace with reference to prev voucher's value */
1347 			prev_val_index = iv_lookup(prev_iv, key_index);
1348 			ivace_reference_by_index(key_index, prev_val_index);
1349 			iv_set(voucher, key_index, prev_val_index);
1350 		}
1351 		break;
1352 
1353 	/*
1354 	 * MACH_VOUCHER_ATTR_REMOVE
1355 	 *	Remove the attribute(s) from the under construction voucher.
1356 	 *	A wildcard key is an acceptable value - indicating a desire
1357 	 *	to remove all the attribute values set up so far in the voucher.
1358 	 *	If a previous voucher is specified, only remove the value it
1359 	 *	it matches the value in the previous voucher.
1360 	 */
1361 	case MACH_VOUCHER_ATTR_REMOVE:
1362 		/* no recipe data on a remove */
1363 		if (0 < content_size) {
1364 			return KERN_INVALID_ARGUMENT;
1365 		}
1366 
1367 		if (MACH_VOUCHER_ATTR_KEY_ALL == key) {
1368 			iv_index_t limit, j;
1369 
1370 			/* reconcile possible difference in voucher sizes */
1371 			limit = (IV_NULL == prev_iv) ? voucher->iv_table_size :
1372 			    ((prev_iv->iv_table_size < voucher->iv_table_size) ?
1373 			    prev_iv->iv_table_size : voucher->iv_table_size);
1374 
1375 			/* wildcard matching */
1376 			for (j = 0; j < limit; j++) {
1377 				val_index = iv_lookup(voucher, j);
1378 
1379 				/* If not matched in previous, skip */
1380 				if (IV_NULL != prev_iv) {
1381 					prev_val_index = iv_lookup(prev_iv, j);
1382 					if (val_index != prev_val_index) {
1383 						continue;
1384 					}
1385 				}
1386 				/* release and clear */
1387 				ivace_release(j, val_index);
1388 				iv_set(voucher, j, IV_UNUSED_VALINDEX);
1389 			}
1390 		} else {
1391 			iv_index_t key_index;
1392 
1393 			/* copy just one key */
1394 			key_index = iv_key_to_index(key);
1395 			if (ivgt_keys_in_use < key_index) {
1396 				return KERN_INVALID_ARGUMENT;
1397 			}
1398 
1399 			val_index = iv_lookup(voucher, key_index);
1400 
1401 			/* If not matched in previous, skip */
1402 			if (IV_NULL != prev_iv) {
1403 				prev_val_index = iv_lookup(prev_iv, key_index);
1404 				if (val_index != prev_val_index) {
1405 					break;
1406 				}
1407 			}
1408 
1409 			/* release and clear */
1410 			ivace_release(key_index, val_index);
1411 			iv_set(voucher, key_index, IV_UNUSED_VALINDEX);
1412 		}
1413 		break;
1414 
1415 	/*
1416 	 * MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
1417 	 *	Use key-privilege to set a value handle for the attribute directly,
1418 	 *	rather than triggering a callback into the attribute manager to
1419 	 *	interpret a recipe to generate the value handle.
1420 	 */
1421 	case MACH_VOUCHER_ATTR_SET_VALUE_HANDLE:
1422 		if (key_priv) {
1423 			mach_voucher_attr_value_handle_t new_value;
1424 
1425 			if (sizeof(mach_voucher_attr_value_handle_t) != content_size) {
1426 				return KERN_INVALID_ARGUMENT;
1427 			}
1428 
1429 			new_value = *(mach_voucher_attr_value_handle_t *)(void *)content;
1430 			kr = ipc_directly_replace_voucher_value(voucher,
1431 			    key, new_value);
1432 			if (KERN_SUCCESS != kr) {
1433 				return kr;
1434 			}
1435 		} else {
1436 			return KERN_INVALID_CAPABILITY;
1437 		}
1438 		break;
1439 
1440 	/*
1441 	 * MACH_VOUCHER_ATTR_REDEEM
1442 	 *	Redeem the attribute(s) from the previous voucher for a possibly
1443 	 *	new value in the new voucher. A wildcard key is an acceptable value,
1444 	 *	indicating a desire to redeem all the values.
1445 	 */
1446 	case MACH_VOUCHER_ATTR_REDEEM:
1447 
1448 		if (MACH_VOUCHER_ATTR_KEY_ALL == key) {
1449 			iv_index_t limit, j;
1450 
1451 			/* reconcile possible difference in voucher sizes */
1452 			if (IV_NULL != prev_iv) {
1453 				limit = (prev_iv->iv_table_size < voucher->iv_table_size) ?
1454 				    prev_iv->iv_table_size :
1455 				    voucher->iv_table_size;
1456 			} else {
1457 				limit = voucher->iv_table_size;
1458 			}
1459 
1460 			/* wildcard matching */
1461 			for (j = 0; j < limit; j++) {
1462 				mach_voucher_attr_key_t j_key;
1463 
1464 				j_key = iv_index_to_key(j);
1465 
1466 				/* skip non-existent managers */
1467 				if (MACH_VOUCHER_ATTR_KEY_NONE == j_key) {
1468 					continue;
1469 				}
1470 
1471 				/* get the new value from redeem (skip empty previous) */
1472 				kr = ipc_replace_voucher_value(voucher,
1473 				    j_key,
1474 				    command,
1475 				    prev_iv,
1476 				    content,
1477 				    content_size);
1478 				if (KERN_SUCCESS != kr) {
1479 					return kr;
1480 				}
1481 			}
1482 			break;
1483 		}
1484 		OS_FALLTHROUGH; /* fall thru for single key redemption */
1485 
1486 	/*
1487 	 * DEFAULT:
1488 	 *	Replace the current value for the <voucher, key> pair with whatever
1489 	 *	value the resource manager returns for the command and recipe
1490 	 *	combination provided.
1491 	 */
1492 	default:
1493 		kr = ipc_replace_voucher_value(voucher,
1494 		    key,
1495 		    command,
1496 		    prev_iv,
1497 		    content,
1498 		    content_size);
1499 		if (KERN_SUCCESS != kr) {
1500 			return kr;
1501 		}
1502 
1503 		break;
1504 	}
1505 	return KERN_SUCCESS;
1506 }
1507 
1508 /*
1509  *	Routine:	iv_checksum
1510  *	Purpose:
1511  *		Compute the voucher sum.  This is more position-
1512  *		relevant than many other checksums - important for
1513  *		vouchers (arrays of low, oft-reused, indexes).
1514  */
1515 static inline iv_index_t
iv_checksum(ipc_voucher_t voucher,boolean_t * emptyp)1516 iv_checksum(ipc_voucher_t voucher, boolean_t *emptyp)
1517 {
1518 	iv_index_t c = 0;
1519 
1520 	boolean_t empty = TRUE;
1521 	if (0 < voucher->iv_table_size) {
1522 		iv_index_t i = voucher->iv_table_size - 1;
1523 
1524 		do {
1525 			iv_index_t v = voucher->iv_table[i];
1526 			c = c << 3 | c >> (32 - 3);             /* rotate */
1527 			c = ~c;                                 /* invert */
1528 			if (0 < v) {
1529 				c += v;                         /* add in */
1530 				empty = FALSE;
1531 			}
1532 		} while (0 < i--);
1533 	}
1534 	*emptyp = empty;
1535 	return c;
1536 }
1537 
1538 /*
1539  *	Routine:	iv_dedup
1540  *	Purpose:
1541  *		See if the set of values represented by this new voucher
1542  *		already exist in another voucher.  If so return a reference
1543  *		to the existing voucher and deallocate the voucher provided.
1544  *		Otherwise, insert this one in the hash and return it.
1545  *	Conditions:
1546  *		A voucher reference is donated on entry.
1547  *	Returns:
1548  *		A voucher reference (may be different than on entry).
1549  */
1550 static ipc_voucher_t
iv_dedup(ipc_voucher_t new_iv)1551 iv_dedup(ipc_voucher_t new_iv)
1552 {
1553 	boolean_t empty;
1554 	iv_index_t sum;
1555 	iv_index_t hash;
1556 	ipc_voucher_t iv;
1557 
1558 	sum = iv_checksum(new_iv, &empty);
1559 
1560 	/* If all values are default, that's the empty (NULL) voucher */
1561 	if (empty) {
1562 		iv_dealloc(new_iv, FALSE);
1563 		return IV_NULL;
1564 	}
1565 
1566 	hash = IV_HASH_BUCKET(sum);
1567 
1568 	ivht_lock();
1569 	queue_iterate(&ivht_bucket[hash], iv, ipc_voucher_t, iv_hash_link) {
1570 		assert(iv->iv_hash == hash);
1571 
1572 		/* if not already deallocating and sums match... */
1573 		if ((os_ref_get_count(&iv->iv_refs) > 0) && (iv->iv_sum == sum)) {
1574 			iv_index_t i;
1575 
1576 			assert(iv->iv_table_size <= new_iv->iv_table_size);
1577 
1578 			/* and common entries match... */
1579 			for (i = 0; i < iv->iv_table_size; i++) {
1580 				if (iv->iv_table[i] != new_iv->iv_table[i]) {
1581 					break;
1582 				}
1583 			}
1584 			if (i < iv->iv_table_size) {
1585 				continue;
1586 			}
1587 
1588 			/* and all extra entries in new one are unused... */
1589 			while (i < new_iv->iv_table_size) {
1590 				if (new_iv->iv_table[i++] != IV_UNUSED_VALINDEX) {
1591 					break;
1592 				}
1593 			}
1594 			if (i < new_iv->iv_table_size) {
1595 				continue;
1596 			}
1597 
1598 			/* ... we found a match... */
1599 
1600 			/* can we get a ref before it hits 0
1601 			 *
1602 			 * This is thread safe. If the reference count is zero before we
1603 			 * adjust it, no other thread can have a reference to the voucher.
1604 			 * The dealloc code requires holding the ivht_lock, so
1605 			 * the voucher cannot be yanked out from under us.
1606 			 */
1607 			if (!os_ref_retain_try(&iv->iv_refs)) {
1608 				continue;
1609 			}
1610 
1611 			ivht_unlock();
1612 
1613 			/* referenced previous, so deallocate the new one */
1614 			iv_dealloc(new_iv, FALSE);
1615 			return iv;
1616 		}
1617 	}
1618 
1619 	/* add the new voucher to the hash, and return it */
1620 	new_iv->iv_sum = sum;
1621 	new_iv->iv_hash = hash;
1622 	queue_enter(&ivht_bucket[hash], new_iv, ipc_voucher_t, iv_hash_link);
1623 	ivht_count++;
1624 	ivht_unlock();
1625 
1626 	/*
1627 	 * This code is disabled for KDEBUG_LEVEL_IST and KDEBUG_LEVEL_NONE
1628 	 */
1629 #if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD)
1630 	if (kdebug_enable & ~KDEBUG_ENABLE_PPT) {
1631 		uintptr_t voucher_addr = VM_KERNEL_ADDRPERM((uintptr_t)new_iv);
1632 		uintptr_t attr_tracepoints_needed = 0;
1633 
1634 		if (ipc_voucher_trace_contents) {
1635 			/*
1636 			 * voucher_contents sizing is a bit more constrained
1637 			 * than might be obvious.
1638 			 *
1639 			 * This is typically a uint8_t typed array. However,
1640 			 * we want to access it as a uintptr_t to efficiently
1641 			 * copyout the data in tracepoints.
1642 			 *
1643 			 * This constrains the size to uintptr_t bytes, and
1644 			 * adds a minimimum alignment requirement equivalent
1645 			 * to a uintptr_t.
1646 			 *
1647 			 * Further constraining the size is the fact that it
1648 			 * is copied out 4 uintptr_t chunks at a time. We do
1649 			 * NOT want to run off the end of the array and copyout
1650 			 * random stack data.
1651 			 *
1652 			 * So the minimum size is 4 * sizeof(uintptr_t), and
1653 			 * the minimum alignment is uintptr_t aligned.
1654 			 */
1655 
1656 #define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
1657 #define PAYLOAD_SIZE 1024
1658 
1659 			static_assert(PAYLOAD_SIZE % PAYLOAD_PER_TRACEPOINT == 0, "size invariant violated");
1660 
1661 			mach_voucher_attr_raw_recipe_array_size_t payload_size = PAYLOAD_SIZE;
1662 			uintptr_t payload[PAYLOAD_SIZE / sizeof(uintptr_t)];
1663 			kern_return_t kr;
1664 
1665 			kr = mach_voucher_extract_all_attr_recipes(new_iv, (mach_voucher_attr_raw_recipe_array_t)payload, &payload_size);
1666 			if (KERN_SUCCESS == kr) {
1667 				attr_tracepoints_needed = (payload_size + PAYLOAD_PER_TRACEPOINT - 1) / PAYLOAD_PER_TRACEPOINT;
1668 
1669 				/*
1670 				 * To prevent leaking data from the stack, we
1671 				 * need to zero data to the end of a tracepoint
1672 				 * payload.
1673 				 */
1674 				size_t remainder = payload_size % PAYLOAD_PER_TRACEPOINT;
1675 				if (remainder) {
1676 					bzero((uint8_t*)payload + payload_size,
1677 					    PAYLOAD_PER_TRACEPOINT - remainder);
1678 				}
1679 			}
1680 
1681 			KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_VOUCHER_CREATE),
1682 			    voucher_addr, new_iv->iv_table_size, ivht_count,
1683 			    payload_size);
1684 
1685 			uintptr_t index = 0;
1686 			while (attr_tracepoints_needed--) {
1687 				KDBG(MACHDBG_CODE(DBG_MACH_IPC,
1688 				    MACH_IPC_VOUCHER_CREATE_ATTR_DATA), payload[index],
1689 				    payload[index + 1], payload[index + 2],
1690 				    payload[index + 3]);
1691 				index += 4;
1692 			}
1693 		} else {
1694 			KDBG(MACHDBG_CODE(DBG_MACH_IPC, MACH_IPC_VOUCHER_CREATE),
1695 			    voucher_addr, new_iv->iv_table_size, ivht_count);
1696 		}
1697 	}
1698 #endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
1699 
1700 	return new_iv;
1701 }
1702 
1703 /*
1704  *	Routine:	ipc_create_mach_voucher
1705  *	Purpose:
1706  *		Create a new mach voucher and initialize it with the
1707  *		value(s) created by having the appropriate resource
1708  *		managers interpret the supplied recipe commands and
1709  *		data.
1710  *	Conditions:
1711  *		Nothing locked (may invoke user-space repeatedly).
1712  *		Caller holds references on previous vouchers.
1713  *		Previous vouchers are passed as voucher indexes.
1714  */
1715 kern_return_t
ipc_create_mach_voucher(ipc_voucher_attr_raw_recipe_array_t recipes,ipc_voucher_attr_raw_recipe_array_size_t recipe_size,ipc_voucher_t * new_voucher)1716 ipc_create_mach_voucher(
1717 	ipc_voucher_attr_raw_recipe_array_t             recipes,
1718 	ipc_voucher_attr_raw_recipe_array_size_t        recipe_size,
1719 	ipc_voucher_t                                   *new_voucher)
1720 {
1721 	ipc_voucher_attr_recipe_t sub_recipe;
1722 	ipc_voucher_attr_recipe_size_t recipe_used = 0;
1723 	ipc_voucher_t voucher;
1724 	kern_return_t kr = KERN_SUCCESS;
1725 
1726 	/* if nothing to do ... */
1727 	if (0 == recipe_size) {
1728 		*new_voucher = IV_NULL;
1729 		return KERN_SUCCESS;
1730 	}
1731 
1732 	/* allocate a voucher */
1733 	voucher = iv_alloc(ivgt_keys_in_use);
1734 	if (IV_NULL == voucher) {
1735 		return KERN_RESOURCE_SHORTAGE;
1736 	}
1737 
1738 	/* iterate over the recipe items */
1739 	while (0 < recipe_size - recipe_used) {
1740 		if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
1741 			kr = KERN_INVALID_ARGUMENT;
1742 			break;
1743 		}
1744 
1745 		/* find the next recipe */
1746 		sub_recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
1747 		if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
1748 			kr = KERN_INVALID_ARGUMENT;
1749 			break;
1750 		}
1751 		recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
1752 
1753 		kr = ipc_execute_voucher_recipe_command(voucher,
1754 		    sub_recipe->key,
1755 		    sub_recipe->command,
1756 		    sub_recipe->previous_voucher,
1757 		    sub_recipe->content,
1758 		    sub_recipe->content_size,
1759 		    FALSE);
1760 		if (KERN_SUCCESS != kr) {
1761 			break;
1762 		}
1763 	}
1764 
1765 	if (KERN_SUCCESS == kr) {
1766 		*new_voucher = iv_dedup(voucher);
1767 	} else {
1768 		iv_dealloc(voucher, FALSE);
1769 		*new_voucher = IV_NULL;
1770 	}
1771 	return kr;
1772 }
1773 
1774 /*
1775  *	Routine:	ipc_voucher_attr_control_create_mach_voucher
1776  *	Purpose:
1777  *		Create a new mach voucher and initialize it with the
1778  *		value(s) created by having the appropriate resource
1779  *		managers interpret the supplied recipe commands and
1780  *		data.
1781  *
1782  *		The resource manager control's privilege over its
1783  *		particular key value is reflected on to the execution
1784  *		code, allowing internal commands (like setting a
1785  *		key value handle directly, rather than having to
1786  *		create a recipe, that will generate a callback just
1787  *		to get the value.
1788  *
1789  *	Conditions:
1790  *		Nothing locked (may invoke user-space repeatedly).
1791  *		Caller holds references on previous vouchers.
1792  *		Previous vouchers are passed as voucher indexes.
1793  */
1794 kern_return_t
ipc_voucher_attr_control_create_mach_voucher(ipc_voucher_attr_control_t control,ipc_voucher_attr_raw_recipe_array_t recipes,ipc_voucher_attr_raw_recipe_array_size_t recipe_size,ipc_voucher_t * new_voucher)1795 ipc_voucher_attr_control_create_mach_voucher(
1796 	ipc_voucher_attr_control_t                      control,
1797 	ipc_voucher_attr_raw_recipe_array_t             recipes,
1798 	ipc_voucher_attr_raw_recipe_array_size_t        recipe_size,
1799 	ipc_voucher_t                                   *new_voucher)
1800 {
1801 	mach_voucher_attr_key_t control_key;
1802 	ipc_voucher_attr_recipe_t sub_recipe;
1803 	ipc_voucher_attr_recipe_size_t recipe_used = 0;
1804 	ipc_voucher_t voucher = IV_NULL;
1805 	kern_return_t kr = KERN_SUCCESS;
1806 
1807 	if (IPC_VOUCHER_ATTR_CONTROL_NULL == control) {
1808 		return KERN_INVALID_CAPABILITY;
1809 	}
1810 
1811 	/* if nothing to do ... */
1812 	if (0 == recipe_size) {
1813 		*new_voucher = IV_NULL;
1814 		return KERN_SUCCESS;
1815 	}
1816 
1817 	/* allocate new voucher */
1818 	voucher = iv_alloc(ivgt_keys_in_use);
1819 	if (IV_NULL == voucher) {
1820 		return KERN_RESOURCE_SHORTAGE;
1821 	}
1822 
1823 	control_key = iv_index_to_key(control->ivac_key_index);
1824 
1825 	/* iterate over the recipe items */
1826 	while (0 < recipe_size - recipe_used) {
1827 		if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
1828 			kr = KERN_INVALID_ARGUMENT;
1829 			break;
1830 		}
1831 
1832 		/* find the next recipe */
1833 		sub_recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
1834 		if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
1835 			kr = KERN_INVALID_ARGUMENT;
1836 			break;
1837 		}
1838 		recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
1839 
1840 		kr = ipc_execute_voucher_recipe_command(voucher,
1841 		    sub_recipe->key,
1842 		    sub_recipe->command,
1843 		    sub_recipe->previous_voucher,
1844 		    sub_recipe->content,
1845 		    sub_recipe->content_size,
1846 		    (sub_recipe->key == control_key));
1847 		if (KERN_SUCCESS != kr) {
1848 			break;
1849 		}
1850 	}
1851 
1852 	if (KERN_SUCCESS == kr) {
1853 		*new_voucher = iv_dedup(voucher);
1854 	} else {
1855 		*new_voucher = IV_NULL;
1856 		iv_dealloc(voucher, FALSE);
1857 	}
1858 	return kr;
1859 }
1860 
1861 /*
1862  *	ipc_register_well_known_mach_voucher_attr_manager
1863  *
1864  *	Register the resource manager responsible for a given key value.
1865  */
1866 kern_return_t
ipc_register_well_known_mach_voucher_attr_manager(ipc_voucher_attr_manager_t manager,mach_voucher_attr_value_handle_t default_value,mach_voucher_attr_key_t key,ipc_voucher_attr_control_t * control)1867 ipc_register_well_known_mach_voucher_attr_manager(
1868 	ipc_voucher_attr_manager_t manager,
1869 	mach_voucher_attr_value_handle_t default_value,
1870 	mach_voucher_attr_key_t key,
1871 	ipc_voucher_attr_control_t *control)
1872 {
1873 	ipc_voucher_attr_control_t new_control;
1874 	iv_index_t key_index;
1875 	iv_index_t hash_index;
1876 
1877 	if (IVAM_NULL == manager) {
1878 		return KERN_INVALID_ARGUMENT;
1879 	}
1880 
1881 	key_index = iv_key_to_index(key);
1882 	if (IV_UNUSED_KEYINDEX == key_index) {
1883 		return KERN_INVALID_ARGUMENT;
1884 	}
1885 
1886 	new_control = ivac_alloc(key_index);
1887 	if (IVAC_NULL == new_control) {
1888 		return KERN_RESOURCE_SHORTAGE;
1889 	}
1890 
1891 	/* insert the default value into slot 0 */
1892 	new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_value = default_value;
1893 	new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_refs = IVACE_REFS_MAX;
1894 	new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_made = IVACE_REFS_MAX;
1895 	new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_persist = TRUE;
1896 	assert(IV_HASH_END == new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_next);
1897 
1898 	ivgt_lock();
1899 	if (IVAM_NULL != iv_global_table[key_index].ivgte_manager) {
1900 		ivgt_unlock();
1901 		ivac_release(new_control);
1902 		return KERN_INVALID_ARGUMENT;
1903 	}
1904 
1905 	/* fill in the global table slot for this key */
1906 	iv_global_table[key_index].ivgte_manager = manager;
1907 	iv_global_table[key_index].ivgte_control = new_control;
1908 	iv_global_table[key_index].ivgte_key = key;
1909 
1910 	/* insert the default value into the hash (in case it is returned later) */
1911 	hash_index = iv_hash_value(key_index, default_value);
1912 	assert(IV_HASH_END == new_control->ivac_table[hash_index].ivace_index);
1913 	ivace_lookup(new_control, hash_index)->ivace_index = IV_UNUSED_VALINDEX;
1914 
1915 	ivgt_unlock();
1916 
1917 	/* return the reference on the new cache control to the caller */
1918 	*control = new_control;
1919 
1920 	return KERN_SUCCESS;
1921 }
1922 
1923 /*
1924  *	Routine:	mach_voucher_extract_attr_content
1925  *	Purpose:
1926  *		Extract the content for a given <voucher, key> pair.
1927  *
1928  *		If a value other than the default is present for this
1929  *		<voucher,key> pair, we need to contact the resource
1930  *		manager to extract the content/meaning of the value(s)
1931  *		present.  Otherwise, return success (but no data).
1932  *
1933  *	Conditions:
1934  *		Nothing locked - as it may upcall to user-space.
1935  *		The caller holds a reference on the voucher.
1936  */
1937 kern_return_t
mach_voucher_extract_attr_content(ipc_voucher_t voucher,mach_voucher_attr_key_t key,mach_voucher_attr_content_t content,mach_voucher_attr_content_size_t * in_out_size)1938 mach_voucher_extract_attr_content(
1939 	ipc_voucher_t                           voucher,
1940 	mach_voucher_attr_key_t                 key,
1941 	mach_voucher_attr_content_t             content,
1942 	mach_voucher_attr_content_size_t        *in_out_size)
1943 {
1944 	mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
1945 	mach_voucher_attr_value_handle_array_size_t vals_count;
1946 	mach_voucher_attr_recipe_command_t command;
1947 	ipc_voucher_attr_manager_t manager;
1948 	iv_index_t value_index;
1949 	iv_index_t key_index;
1950 	kern_return_t kr;
1951 
1952 
1953 	if (IV_NULL == voucher) {
1954 		return KERN_INVALID_ARGUMENT;
1955 	}
1956 
1957 	key_index = iv_key_to_index(key);
1958 
1959 	value_index = iv_lookup(voucher, key_index);
1960 	if (IV_UNUSED_VALINDEX == value_index) {
1961 		*in_out_size = 0;
1962 		return KERN_SUCCESS;
1963 	}
1964 
1965 	/*
1966 	 * Get the manager for this key_index.  The
1967 	 * existence of a non-default value for this
1968 	 * slot within our voucher will keep the
1969 	 * manager referenced during the callout.
1970 	 */
1971 	ivgt_lookup(key_index, FALSE, &manager, NULL);
1972 	if (IVAM_NULL == manager) {
1973 		return KERN_INVALID_ARGUMENT;
1974 	}
1975 
1976 	/*
1977 	 * Get the value(s) to pass to the manager
1978 	 * for this value_index.
1979 	 */
1980 	ivace_lookup_values(key_index, value_index,
1981 	    vals, &vals_count);
1982 	assert(0 < vals_count);
1983 
1984 	/* callout to manager */
1985 
1986 	kr = (manager->ivam_extract_content)(manager, key,
1987 	    vals, vals_count, &command, content, in_out_size);
1988 	return kr;
1989 }
1990 
1991 /*
1992  *	Routine:	mach_voucher_extract_attr_recipe
1993  *	Purpose:
1994  *		Extract a recipe for a given <voucher, key> pair.
1995  *
1996  *		If a value other than the default is present for this
1997  *		<voucher,key> pair, we need to contact the resource
1998  *		manager to extract the content/meaning of the value(s)
1999  *		present.  Otherwise, return success (but no data).
2000  *
2001  *	Conditions:
2002  *		Nothing locked - as it may upcall to user-space.
2003  *		The caller holds a reference on the voucher.
2004  */
2005 kern_return_t
mach_voucher_extract_attr_recipe(ipc_voucher_t voucher,mach_voucher_attr_key_t key,mach_voucher_attr_raw_recipe_t raw_recipe,mach_voucher_attr_raw_recipe_size_t * in_out_size)2006 mach_voucher_extract_attr_recipe(
2007 	ipc_voucher_t                           voucher,
2008 	mach_voucher_attr_key_t                 key,
2009 	mach_voucher_attr_raw_recipe_t          raw_recipe,
2010 	mach_voucher_attr_raw_recipe_size_t     *in_out_size)
2011 {
2012 	mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2013 	mach_voucher_attr_value_handle_array_size_t vals_count;
2014 	ipc_voucher_attr_manager_t manager;
2015 	mach_voucher_attr_recipe_t recipe;
2016 	iv_index_t value_index;
2017 	iv_index_t key_index;
2018 	kern_return_t kr;
2019 
2020 
2021 	if (IV_NULL == voucher) {
2022 		return KERN_INVALID_ARGUMENT;
2023 	}
2024 
2025 	key_index = iv_key_to_index(key);
2026 
2027 	value_index = iv_lookup(voucher, key_index);
2028 	if (IV_UNUSED_VALINDEX == value_index) {
2029 		*in_out_size = 0;
2030 		return KERN_SUCCESS;
2031 	}
2032 
2033 	if (*in_out_size < sizeof(*recipe)) {
2034 		return KERN_NO_SPACE;
2035 	}
2036 
2037 	recipe = (mach_voucher_attr_recipe_t)(void *)raw_recipe;
2038 	recipe->key = key;
2039 	recipe->command = MACH_VOUCHER_ATTR_NOOP;
2040 	recipe->previous_voucher = MACH_VOUCHER_NAME_NULL;
2041 	recipe->content_size = *in_out_size - sizeof(*recipe);
2042 
2043 	/*
2044 	 * Get the manager for this key_index.  The
2045 	 * existence of a non-default value for this
2046 	 * slot within our voucher will keep the
2047 	 * manager referenced during the callout.
2048 	 */
2049 	ivgt_lookup(key_index, FALSE, &manager, NULL);
2050 	if (IVAM_NULL == manager) {
2051 		return KERN_INVALID_ARGUMENT;
2052 	}
2053 
2054 	/*
2055 	 * Get the value(s) to pass to the manager
2056 	 * for this value_index.
2057 	 */
2058 	ivace_lookup_values(key_index, value_index,
2059 	    vals, &vals_count);
2060 	assert(0 < vals_count);
2061 
2062 	/* callout to manager */
2063 	kr = (manager->ivam_extract_content)(manager, key,
2064 	    vals, vals_count,
2065 	    &recipe->command,
2066 	    recipe->content, &recipe->content_size);
2067 	if (KERN_SUCCESS == kr) {
2068 		assert(*in_out_size - sizeof(*recipe) >= recipe->content_size);
2069 		*in_out_size = sizeof(*recipe) + recipe->content_size;
2070 	}
2071 
2072 	return kr;
2073 }
2074 
2075 
2076 
2077 /*
2078  *	Routine:	mach_voucher_extract_all_attr_recipes
2079  *	Purpose:
2080  *		Extract all the (non-default) contents for a given voucher,
2081  *		building up a recipe that could be provided to a future
2082  *		voucher creation call.
2083  *	Conditions:
2084  *		Nothing locked (may invoke user-space).
2085  *		Caller holds a reference on the supplied voucher.
2086  */
2087 kern_return_t
mach_voucher_extract_all_attr_recipes(ipc_voucher_t voucher,mach_voucher_attr_raw_recipe_array_t recipes,mach_voucher_attr_raw_recipe_array_size_t * in_out_size)2088 mach_voucher_extract_all_attr_recipes(
2089 	ipc_voucher_t                                   voucher,
2090 	mach_voucher_attr_raw_recipe_array_t            recipes,
2091 	mach_voucher_attr_raw_recipe_array_size_t       *in_out_size)
2092 {
2093 	mach_voucher_attr_recipe_size_t recipe_size = *in_out_size;
2094 	mach_voucher_attr_recipe_size_t recipe_used = 0;
2095 	iv_index_t key_index;
2096 
2097 	if (IV_NULL == voucher) {
2098 		return KERN_INVALID_ARGUMENT;
2099 	}
2100 
2101 	for (key_index = 0; key_index < voucher->iv_table_size; key_index++) {
2102 		mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2103 		mach_voucher_attr_value_handle_array_size_t vals_count;
2104 		mach_voucher_attr_content_size_t content_size;
2105 		ipc_voucher_attr_manager_t manager;
2106 		mach_voucher_attr_recipe_t recipe;
2107 		mach_voucher_attr_key_t key;
2108 		iv_index_t value_index;
2109 		kern_return_t kr;
2110 
2111 		/* don't output anything for a default value */
2112 		value_index = iv_lookup(voucher, key_index);
2113 		if (IV_UNUSED_VALINDEX == value_index) {
2114 			continue;
2115 		}
2116 
2117 		if (recipe_size - recipe_used < sizeof(*recipe)) {
2118 			return KERN_NO_SPACE;
2119 		}
2120 
2121 		/*
2122 		 * Get the manager for this key_index.  The
2123 		 * existence of a non-default value for this
2124 		 * slot within our voucher will keep the
2125 		 * manager referenced during the callout.
2126 		 */
2127 		ivgt_lookup(key_index, FALSE, &manager, NULL);
2128 		assert(IVAM_NULL != manager);
2129 		if (IVAM_NULL == manager) {
2130 			continue;
2131 		}
2132 
2133 		recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2134 		content_size = recipe_size - recipe_used - sizeof(*recipe);
2135 
2136 		/*
2137 		 * Get the value(s) to pass to the manager
2138 		 * for this value_index.
2139 		 */
2140 		ivace_lookup_values(key_index, value_index,
2141 		    vals, &vals_count);
2142 		assert(0 < vals_count);
2143 
2144 		key = iv_index_to_key(key_index);
2145 
2146 		recipe->key = key;
2147 		recipe->command = MACH_VOUCHER_ATTR_NOOP;
2148 		recipe->content_size = content_size;
2149 
2150 		/* callout to manager */
2151 		kr = (manager->ivam_extract_content)(manager, key,
2152 		    vals, vals_count,
2153 		    &recipe->command,
2154 		    recipe->content, &recipe->content_size);
2155 		if (KERN_SUCCESS != kr) {
2156 			return kr;
2157 		}
2158 
2159 		assert(recipe->content_size <= content_size);
2160 		recipe_used += sizeof(*recipe) + recipe->content_size;
2161 	}
2162 
2163 	*in_out_size = recipe_used;
2164 	return KERN_SUCCESS;
2165 }
2166 
2167 /*
2168  *	Routine:	mach_voucher_debug_info
2169  *	Purpose:
2170  *		Extract all the (non-default) contents for a given mach port name,
2171  *		building up a recipe that could be provided to a future
2172  *		voucher creation call.
2173  *	Conditions:
2174  *		Nothing locked (may invoke user-space).
2175  *		Caller may not hold a reference on the supplied voucher.
2176  */
2177 #if !(DEVELOPMENT || DEBUG)
2178 kern_return_t
mach_voucher_debug_info(ipc_space_t __unused space,mach_port_name_t __unused voucher_name,mach_voucher_attr_raw_recipe_array_t __unused recipes,mach_voucher_attr_raw_recipe_array_size_t __unused * in_out_size)2179 mach_voucher_debug_info(
2180 	ipc_space_t                                     __unused space,
2181 	mach_port_name_t                                __unused voucher_name,
2182 	mach_voucher_attr_raw_recipe_array_t            __unused recipes,
2183 	mach_voucher_attr_raw_recipe_array_size_t       __unused *in_out_size)
2184 {
2185 	return KERN_NOT_SUPPORTED;
2186 }
2187 #else
2188 kern_return_t
mach_voucher_debug_info(ipc_space_t space,mach_port_name_t voucher_name,mach_voucher_attr_raw_recipe_array_t recipes,mach_voucher_attr_raw_recipe_array_size_t * in_out_size)2189 mach_voucher_debug_info(
2190 	ipc_space_t                                     space,
2191 	mach_port_name_t                                voucher_name,
2192 	mach_voucher_attr_raw_recipe_array_t            recipes,
2193 	mach_voucher_attr_raw_recipe_array_size_t       *in_out_size)
2194 {
2195 	ipc_voucher_t voucher = IPC_VOUCHER_NULL;
2196 	kern_return_t kr;
2197 	ipc_port_t port = MACH_PORT_NULL;
2198 
2199 	if (space == IS_NULL) {
2200 		return KERN_INVALID_TASK;
2201 	}
2202 
2203 	if (!MACH_PORT_VALID(voucher_name)) {
2204 		return KERN_INVALID_ARGUMENT;
2205 	}
2206 
2207 	kr = ipc_port_translate_send(space, voucher_name, &port);
2208 	if (KERN_SUCCESS != kr) {
2209 		return KERN_INVALID_ARGUMENT;
2210 	}
2211 
2212 	voucher = convert_port_to_voucher(port);
2213 	ip_mq_unlock(port);
2214 
2215 	if (voucher) {
2216 		kr = mach_voucher_extract_all_attr_recipes(voucher, recipes, in_out_size);
2217 		ipc_voucher_release(voucher);
2218 		return kr;
2219 	}
2220 
2221 	return KERN_FAILURE;
2222 }
2223 #endif
2224 
2225 /*
2226  *	Routine:	mach_voucher_attr_command
2227  *	Purpose:
2228  *		Invoke an attribute-specific command through this voucher.
2229  *
2230  *		The voucher layout, membership, etc... is not altered
2231  *		through the execution of this command.
2232  *
2233  *	Conditions:
2234  *		Nothing locked - as it may upcall to user-space.
2235  *		The caller holds a reference on the voucher.
2236  */
2237 kern_return_t
mach_voucher_attr_command(ipc_voucher_t voucher,mach_voucher_attr_key_t key,mach_voucher_attr_command_t command,mach_voucher_attr_content_t in_content,mach_voucher_attr_content_size_t in_content_size,mach_voucher_attr_content_t out_content,mach_voucher_attr_content_size_t * out_content_size)2238 mach_voucher_attr_command(
2239 	ipc_voucher_t                                           voucher,
2240 	mach_voucher_attr_key_t                         key,
2241 	mach_voucher_attr_command_t                     command,
2242 	mach_voucher_attr_content_t                     in_content,
2243 	mach_voucher_attr_content_size_t        in_content_size,
2244 	mach_voucher_attr_content_t                     out_content,
2245 	mach_voucher_attr_content_size_t        *out_content_size)
2246 {
2247 	mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2248 	mach_voucher_attr_value_handle_array_size_t vals_count;
2249 	ipc_voucher_attr_manager_t manager;
2250 	ipc_voucher_attr_control_t control;
2251 	iv_index_t value_index;
2252 	iv_index_t key_index;
2253 	kern_return_t kr;
2254 
2255 
2256 	if (IV_NULL == voucher) {
2257 		return KERN_INVALID_ARGUMENT;
2258 	}
2259 
2260 	key_index = iv_key_to_index(key);
2261 
2262 	/*
2263 	 * Get the manager for this key_index.
2264 	 * Allowing commands against the default value
2265 	 * for an attribute means that we have to hold
2266 	 * reference on the attribute manager control
2267 	 * to keep the manager around during the command
2268 	 * execution.
2269 	 */
2270 	ivgt_lookup(key_index, TRUE, &manager, &control);
2271 	if (IVAM_NULL == manager) {
2272 		return KERN_INVALID_ARGUMENT;
2273 	}
2274 
2275 	/*
2276 	 * Get the values for this <voucher, key> pair
2277 	 * to pass to the attribute manager.  It is still
2278 	 * permissible to execute a command against the
2279 	 * default value (empty value array).
2280 	 */
2281 	value_index = iv_lookup(voucher, key_index);
2282 	ivace_lookup_values(key_index, value_index,
2283 	    vals, &vals_count);
2284 
2285 	/* callout to manager */
2286 	kr = (manager->ivam_command)(manager, key,
2287 	    vals, vals_count,
2288 	    command,
2289 	    in_content, in_content_size,
2290 	    out_content, out_content_size);
2291 
2292 	/* release reference on control */
2293 	ivac_release(control);
2294 
2295 	return kr;
2296 }
2297 
2298 /*
2299  *	Routine:	mach_voucher_attr_control_get_values
2300  *	Purpose:
2301  *		For a given voucher, get the value handle associated with the
2302  *		specified attribute manager.
2303  */
2304 kern_return_t
mach_voucher_attr_control_get_values(ipc_voucher_attr_control_t control,ipc_voucher_t voucher,mach_voucher_attr_value_handle_array_t out_values,mach_voucher_attr_value_handle_array_size_t * in_out_size)2305 mach_voucher_attr_control_get_values(
2306 	ipc_voucher_attr_control_t control,
2307 	ipc_voucher_t voucher,
2308 	mach_voucher_attr_value_handle_array_t out_values,
2309 	mach_voucher_attr_value_handle_array_size_t *in_out_size)
2310 {
2311 	iv_index_t key_index, value_index;
2312 
2313 	if (IPC_VOUCHER_ATTR_CONTROL_NULL == control) {
2314 		return KERN_INVALID_CAPABILITY;
2315 	}
2316 
2317 	if (IV_NULL == voucher) {
2318 		return KERN_INVALID_ARGUMENT;
2319 	}
2320 
2321 	if (0 == *in_out_size) {
2322 		return KERN_SUCCESS;
2323 	}
2324 
2325 	key_index = control->ivac_key_index;
2326 
2327 	assert(os_ref_get_count(&voucher->iv_refs) > 0);
2328 	value_index = iv_lookup(voucher, key_index);
2329 	ivace_lookup_values(key_index, value_index,
2330 	    out_values, in_out_size);
2331 	return KERN_SUCCESS;
2332 }
2333 
2334 /*
2335  *	Routine:	mach_voucher_attr_control_create_mach_voucher
2336  *	Purpose:
2337  *		Create a new mach voucher and initialize it by processing the
2338  *		supplied recipe(s).
2339  *
2340  *		Coming in on the attribute control port denotes special privileges
2341  *		over they key associated with the control port.
2342  *
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.
2349  */
2350 kern_return_t
mach_voucher_attr_control_create_mach_voucher(ipc_voucher_attr_control_t control,mach_voucher_attr_raw_recipe_array_t recipes,mach_voucher_attr_raw_recipe_size_t recipe_size,ipc_voucher_t * new_voucher)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)
2356 {
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;
2362 
2363 	if (IPC_VOUCHER_ATTR_CONTROL_NULL == control) {
2364 		return KERN_INVALID_CAPABILITY;
2365 	}
2366 
2367 	/* if nothing to do ... */
2368 	if (0 == recipe_size) {
2369 		*new_voucher = IV_NULL;
2370 		return KERN_SUCCESS;
2371 	}
2372 
2373 	/* allocate new voucher */
2374 	voucher = iv_alloc(ivgt_keys_in_use);
2375 	if (IV_NULL == voucher) {
2376 		return KERN_RESOURCE_SHORTAGE;
2377 	}
2378 
2379 	control_key = iv_index_to_key(control->ivac_key_index);
2380 
2381 	/* iterate over the recipe items */
2382 	while (0 < recipe_size - recipe_used) {
2383 		ipc_voucher_t prev_iv;
2384 
2385 		if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
2386 			kr = KERN_INVALID_ARGUMENT;
2387 			break;
2388 		}
2389 
2390 		/* find the next recipe */
2391 		sub_recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2392 		if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
2393 			kr = KERN_INVALID_ARGUMENT;
2394 			break;
2395 		}
2396 		recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
2397 
2398 		/* convert voucher port name (current space) into a voucher reference */
2399 		prev_iv = convert_port_name_to_voucher(sub_recipe->previous_voucher);
2400 		if (MACH_PORT_NULL != sub_recipe->previous_voucher && IV_NULL == prev_iv) {
2401 			kr = KERN_INVALID_CAPABILITY;
2402 			break;
2403 		}
2404 
2405 		kr = ipc_execute_voucher_recipe_command(voucher,
2406 		    sub_recipe->key,
2407 		    sub_recipe->command,
2408 		    prev_iv,
2409 		    sub_recipe->content,
2410 		    sub_recipe->content_size,
2411 		    (sub_recipe->key == control_key));
2412 		ipc_voucher_release(prev_iv);
2413 
2414 		if (KERN_SUCCESS != kr) {
2415 			break;
2416 		}
2417 	}
2418 
2419 	if (KERN_SUCCESS == kr) {
2420 		*new_voucher = iv_dedup(voucher);
2421 	} else {
2422 		*new_voucher = IV_NULL;
2423 		iv_dealloc(voucher, FALSE);
2424 	}
2425 	return kr;
2426 }
2427 
2428 /*
2429  *	Routine:	host_create_mach_voucher
2430  *	Purpose:
2431  *		Create a new mach voucher and initialize it by processing the
2432  *		supplied recipe(s).
2433  *
2434  *		Comming in from user-space, each recipe item will have a previous
2435  *		recipe port name that needs to be converted to a voucher.  Because
2436  *		we can't rely on the port namespace to hold a reference on each
2437  *		previous voucher port for the duration of processing that command,
2438  *		we have to convert the name to a voucher reference and release it
2439  *		after the command processing is done.
2440  */
2441 kern_return_t
host_create_mach_voucher(host_t host,mach_voucher_attr_raw_recipe_array_t recipes,mach_voucher_attr_raw_recipe_size_t recipe_size,ipc_voucher_t * new_voucher)2442 host_create_mach_voucher(
2443 	host_t host,
2444 	mach_voucher_attr_raw_recipe_array_t recipes,
2445 	mach_voucher_attr_raw_recipe_size_t recipe_size,
2446 	ipc_voucher_t *new_voucher)
2447 {
2448 	mach_voucher_attr_recipe_t sub_recipe;
2449 	mach_voucher_attr_recipe_size_t recipe_used = 0;
2450 	ipc_voucher_t voucher = IV_NULL;
2451 	kern_return_t kr = KERN_SUCCESS;
2452 
2453 	if (host == HOST_NULL) {
2454 		return KERN_INVALID_ARGUMENT;
2455 	}
2456 
2457 	/* if nothing to do ... */
2458 	if (0 == recipe_size) {
2459 		*new_voucher = IV_NULL;
2460 		return KERN_SUCCESS;
2461 	}
2462 
2463 	/* allocate new voucher */
2464 	voucher = iv_alloc(ivgt_keys_in_use);
2465 	if (IV_NULL == voucher) {
2466 		return KERN_RESOURCE_SHORTAGE;
2467 	}
2468 
2469 	/* iterate over the recipe items */
2470 	while (0 < recipe_size - recipe_used) {
2471 		ipc_voucher_t prev_iv;
2472 
2473 		if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
2474 			kr = KERN_INVALID_ARGUMENT;
2475 			break;
2476 		}
2477 
2478 		/* find the next recipe */
2479 		sub_recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2480 		if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
2481 			kr = KERN_INVALID_ARGUMENT;
2482 			break;
2483 		}
2484 		recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
2485 
2486 		/* convert voucher port name (current space) into a voucher reference */
2487 		prev_iv = convert_port_name_to_voucher(sub_recipe->previous_voucher);
2488 		if (MACH_PORT_NULL != sub_recipe->previous_voucher && IV_NULL == prev_iv) {
2489 			kr = KERN_INVALID_CAPABILITY;
2490 			break;
2491 		}
2492 
2493 		kr = ipc_execute_voucher_recipe_command(voucher,
2494 		    sub_recipe->key,
2495 		    sub_recipe->command,
2496 		    prev_iv,
2497 		    sub_recipe->content,
2498 		    sub_recipe->content_size,
2499 		    FALSE);
2500 		ipc_voucher_release(prev_iv);
2501 
2502 		if (KERN_SUCCESS != kr) {
2503 			break;
2504 		}
2505 	}
2506 
2507 	if (KERN_SUCCESS == kr) {
2508 		*new_voucher = iv_dedup(voucher);
2509 	} else {
2510 		*new_voucher = IV_NULL;
2511 		iv_dealloc(voucher, FALSE);
2512 	}
2513 	return kr;
2514 }
2515 
2516 /*
2517  *	Routine:	host_register_well_known_mach_voucher_attr_manager
2518  *	Purpose:
2519  *		Register the user-level resource manager responsible for a given
2520  *		key value.
2521  *	Conditions:
2522  *		The manager port passed in has to be converted/wrapped
2523  *		in an ipc_voucher_attr_manager_t structure and then call the
2524  *		internal variant.  We have a generic ipc voucher manager
2525  *		type that implements a MIG proxy out to user-space just for
2526  *		this purpose.
2527  */
2528 kern_return_t
host_register_well_known_mach_voucher_attr_manager(host_t host,mach_voucher_attr_manager_t __unused manager,mach_voucher_attr_value_handle_t __unused default_value,mach_voucher_attr_key_t __unused key,ipc_voucher_attr_control_t __unused * control)2529 host_register_well_known_mach_voucher_attr_manager(
2530 	host_t host,
2531 	mach_voucher_attr_manager_t __unused manager,
2532 	mach_voucher_attr_value_handle_t __unused default_value,
2533 	mach_voucher_attr_key_t __unused key,
2534 	ipc_voucher_attr_control_t __unused *control)
2535 {
2536 	if (HOST_NULL == host) {
2537 		return KERN_INVALID_HOST;
2538 	}
2539 
2540 #if 1
2541 	return KERN_NOT_SUPPORTED;
2542 #else
2543 	/*
2544 	 * Allocate a mig_voucher_attr_manager_t that provides the
2545 	 * MIG proxy functions for the three manager callbacks and
2546 	 * store the port right in there.
2547 	 *
2548 	 * If the user-space manager dies, we'll detect it on our
2549 	 * next upcall, and cleanup the proxy at that point.
2550 	 */
2551 	mig_voucher_attr_manager_t proxy;
2552 	kern_return_t kr;
2553 
2554 	proxy = mvam_alloc(manager);
2555 
2556 	kr = ipc_register_well_known_mach_voucher_attr_manager(&proxy->mvam_manager,
2557 	    default_value,
2558 	    key,
2559 	    control);
2560 	if (KERN_SUCCESS != kr) {
2561 		mvam_release(proxy);
2562 	}
2563 
2564 	return kr;
2565 #endif
2566 }
2567 
2568 /*
2569  *	Routine:	host_register_mach_voucher_attr_manager
2570  *	Purpose:
2571  *		Register the user-space resource manager and return a
2572  *		dynamically allocated key.
2573  *	Conditions:
2574  *		Wrap the supplied port with the MIG proxy ipc
2575  *		voucher resource manager, and then call the internal
2576  *		variant.
2577  */
2578 kern_return_t
host_register_mach_voucher_attr_manager(host_t host,mach_voucher_attr_manager_t __unused manager,mach_voucher_attr_value_handle_t __unused default_value,mach_voucher_attr_key_t __unused * key,ipc_voucher_attr_control_t __unused * control)2579 host_register_mach_voucher_attr_manager(
2580 	host_t host,
2581 	mach_voucher_attr_manager_t __unused manager,
2582 	mach_voucher_attr_value_handle_t __unused default_value,
2583 	mach_voucher_attr_key_t __unused *key,
2584 	ipc_voucher_attr_control_t __unused *control)
2585 {
2586 	if (HOST_NULL == host) {
2587 		return KERN_INVALID_HOST;
2588 	}
2589 
2590 	return KERN_NOT_SUPPORTED;
2591 }
2592 
2593 /*
2594  *	Routine:	ipc_get_pthpriority_from_kmsg_voucher
2595  *	Purpose:
2596  *		Get the canonicalized pthread priority from the voucher attached in the kmsg.
2597  */
2598 kern_return_t
ipc_get_pthpriority_from_kmsg_voucher(ipc_kmsg_t kmsg,ipc_pthread_priority_value_t * canonicalize_priority_value)2599 ipc_get_pthpriority_from_kmsg_voucher(
2600 	ipc_kmsg_t kmsg,
2601 	ipc_pthread_priority_value_t *canonicalize_priority_value)
2602 {
2603 	mach_port_t voucher_port;
2604 	ipc_voucher_t pthread_priority_voucher;
2605 	mach_voucher_attr_raw_recipe_size_t content_size =
2606 	    sizeof(mach_voucher_attr_recipe_data_t) + sizeof(ipc_pthread_priority_value_t);
2607 	uint8_t content_data[content_size];
2608 	mach_voucher_attr_recipe_t cur_content;
2609 
2610 	kern_return_t kr = KERN_SUCCESS;
2611 
2612 	voucher_port = ipc_kmsg_get_voucher_port(kmsg);
2613 	if (!IP_VALID(voucher_port)) {
2614 		return KERN_FAILURE;
2615 	}
2616 
2617 	pthread_priority_voucher = ip_get_voucher(voucher_port);
2618 	kr = mach_voucher_extract_attr_recipe(pthread_priority_voucher,
2619 	    MACH_VOUCHER_ATTR_KEY_PTHPRIORITY,
2620 	    content_data,
2621 	    &content_size);
2622 	if (kr != KERN_SUCCESS) {
2623 		return kr;
2624 	}
2625 
2626 	/* return KERN_INVALID_VALUE for default value */
2627 	if (content_size < sizeof(mach_voucher_attr_recipe_t)) {
2628 		return KERN_INVALID_VALUE;
2629 	}
2630 
2631 	cur_content = (mach_voucher_attr_recipe_t) (void *) &content_data[0];
2632 	assert(cur_content->content_size == sizeof(ipc_pthread_priority_value_t));
2633 	memcpy(canonicalize_priority_value, cur_content->content, sizeof(ipc_pthread_priority_value_t));
2634 
2635 	return KERN_SUCCESS;
2636 }
2637 
2638 /*
2639  *	Routine:	ipc_voucher_get_default_voucher
2640  *	Purpose:
2641  *		Creates process default voucher and returns it.
2642  */
2643 ipc_voucher_t
ipc_voucher_get_default_voucher(void)2644 ipc_voucher_get_default_voucher(void)
2645 {
2646 	uint8_t recipes[sizeof(ipc_voucher_attr_recipe_data_t)];
2647 	ipc_voucher_attr_recipe_t recipe;
2648 	ipc_voucher_attr_raw_recipe_array_size_t recipe_size = sizeof(ipc_voucher_attr_recipe_data_t);
2649 	kern_return_t kr;
2650 	ipc_voucher_t recv_voucher = IPC_VOUCHER_NULL;
2651 	task_t task = current_task();
2652 
2653 	if (task == kernel_task || task->bank_context == NULL) {
2654 		return IPC_VOUCHER_NULL;
2655 	}
2656 
2657 	recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[0];
2658 	recipe->key = MACH_VOUCHER_ATTR_KEY_BANK;
2659 	recipe->command = MACH_VOUCHER_ATTR_BANK_CREATE;
2660 	recipe->previous_voucher = IPC_VOUCHER_NULL;
2661 	recipe->content_size = 0;
2662 
2663 	kr = ipc_create_mach_voucher(recipes,
2664 	    recipe_size,
2665 	    &recv_voucher);
2666 	assert(KERN_SUCCESS == kr);
2667 
2668 	return recv_voucher;
2669 }
2670 
2671 /*
2672  *	Routine:	ipc_voucher_send_preprocessing
2673  *	Purpose:
2674  *		Processing of the voucher in the kmsg before sending it.
2675  *		Currently use to switch PERSONA_TOKEN in case of process with
2676  *		no com.apple.private.personas.propagate entitlement.
2677  */
2678 void
ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg)2679 ipc_voucher_send_preprocessing(ipc_kmsg_t kmsg)
2680 {
2681 	uint8_t recipes[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) * sizeof(ipc_voucher_attr_recipe_data_t)];
2682 	ipc_voucher_attr_raw_recipe_array_size_t recipe_size = (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) *
2683 	    sizeof(ipc_voucher_attr_recipe_data_t);
2684 	ipc_voucher_t pre_processed_voucher;
2685 	ipc_voucher_t voucher_to_send;
2686 	ipc_port_t voucher_port;
2687 	kern_return_t kr;
2688 	int need_preprocessing = FALSE;
2689 
2690 	voucher_port = ipc_kmsg_get_voucher_port(kmsg);
2691 	if (!IP_VALID(voucher_port) || current_task() == kernel_task) {
2692 		return;
2693 	}
2694 
2695 	/* setup recipe for preprocessing of all the attributes. */
2696 	pre_processed_voucher = ip_get_voucher(voucher_port);
2697 
2698 	kr = ipc_voucher_prepare_processing_recipe(pre_processed_voucher,
2699 	    (mach_voucher_attr_raw_recipe_array_t)recipes,
2700 	    &recipe_size, MACH_VOUCHER_ATTR_SEND_PREPROCESS,
2701 	    IVAM_FLAGS_SUPPORT_SEND_PREPROCESS, &need_preprocessing);
2702 
2703 	assert(KERN_SUCCESS == kr);
2704 	/*
2705 	 * Only do send preprocessing if the voucher needs any pre processing.
2706 	 * Replace the voucher port in the kmsg, but preserve the original type.
2707 	 */
2708 	if (need_preprocessing) {
2709 		kr = ipc_create_mach_voucher(recipes,
2710 		    recipe_size,
2711 		    &voucher_to_send);
2712 		assert(KERN_SUCCESS == kr);
2713 		ipc_port_release_send(voucher_port);
2714 		voucher_port = convert_voucher_to_port(voucher_to_send);
2715 		ipc_kmsg_set_voucher_port(kmsg, voucher_port, kmsg->ikm_voucher_type);
2716 	}
2717 }
2718 
2719 /*
2720  *	Routine:	ipc_voucher_receive_postprocessing
2721  *	Purpose:
2722  *		Redeems the voucher attached to the kmsg.
2723  *	Note:
2724  *		Although it is possible to call ipc_importance_receive
2725  *		here, it is called in mach_msg_receive_results and not here
2726  *		in order to maintain symmetry with ipc_voucher_send_preprocessing.
2727  */
2728 void
ipc_voucher_receive_postprocessing(ipc_kmsg_t kmsg,mach_msg_option_t option)2729 ipc_voucher_receive_postprocessing(
2730 	ipc_kmsg_t              kmsg,
2731 	mach_msg_option_t       option)
2732 {
2733 	uint8_t recipes[(MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) * sizeof(ipc_voucher_attr_recipe_data_t)];
2734 	ipc_voucher_attr_raw_recipe_array_size_t recipe_size = (MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN + 1) *
2735 	    sizeof(ipc_voucher_attr_recipe_data_t);
2736 	ipc_voucher_t recv_voucher;
2737 	ipc_voucher_t sent_voucher;
2738 	ipc_port_t voucher_port;
2739 	kern_return_t kr;
2740 	int need_postprocessing = FALSE;
2741 
2742 	voucher_port = ipc_kmsg_get_voucher_port(kmsg);
2743 	if ((option & MACH_RCV_VOUCHER) == 0 || (!IP_VALID(voucher_port)) ||
2744 	    current_task() == kernel_task) {
2745 		return;
2746 	}
2747 
2748 	/* setup recipe for auto redeem of all the attributes. */
2749 	sent_voucher = ip_get_voucher(voucher_port);
2750 
2751 	kr = ipc_voucher_prepare_processing_recipe(sent_voucher,
2752 	    (mach_voucher_attr_raw_recipe_array_t)recipes,
2753 	    &recipe_size, MACH_VOUCHER_ATTR_AUTO_REDEEM,
2754 	    IVAM_FLAGS_SUPPORT_RECEIVE_POSTPROCESS, &need_postprocessing);
2755 
2756 	assert(KERN_SUCCESS == kr);
2757 
2758 	/*
2759 	 * Only do receive postprocessing if the voucher needs any post processing.
2760 	 */
2761 	if (need_postprocessing) {
2762 		kr = ipc_create_mach_voucher(recipes,
2763 		    recipe_size,
2764 		    &recv_voucher);
2765 		assert(KERN_SUCCESS == kr);
2766 		/* swap the voucher port (and set voucher bits in case it didn't already exist) */
2767 		kmsg->ikm_header->msgh_bits |= (MACH_MSG_TYPE_MOVE_SEND << 16);
2768 		ipc_port_release_send(voucher_port);
2769 		voucher_port = convert_voucher_to_port(recv_voucher);
2770 		ipc_kmsg_set_voucher_port(kmsg, voucher_port, MACH_MSG_TYPE_MOVE_SEND);
2771 	}
2772 }
2773 
2774 /*
2775  *	Routine:	ipc_voucher_prepare_processing_recipe
2776  *	Purpose:
2777  *		Check if the given voucher has an attribute which supports
2778  *		the given flag and prepare a recipe to apply that supported
2779  *		command.
2780  */
2781 static kern_return_t
ipc_voucher_prepare_processing_recipe(ipc_voucher_t voucher,ipc_voucher_attr_raw_recipe_array_t recipes,ipc_voucher_attr_raw_recipe_array_size_t * in_out_size,mach_voucher_attr_recipe_command_t command,ipc_voucher_attr_manager_flags flags,int * need_processing)2782 ipc_voucher_prepare_processing_recipe(
2783 	ipc_voucher_t voucher,
2784 	ipc_voucher_attr_raw_recipe_array_t recipes,
2785 	ipc_voucher_attr_raw_recipe_array_size_t *in_out_size,
2786 	mach_voucher_attr_recipe_command_t command,
2787 	ipc_voucher_attr_manager_flags flags,
2788 	int *need_processing)
2789 {
2790 	ipc_voucher_attr_raw_recipe_array_size_t recipe_size = *in_out_size;
2791 	ipc_voucher_attr_raw_recipe_array_size_t recipe_used = 0;
2792 	iv_index_t key_index;
2793 	ipc_voucher_attr_recipe_t recipe;
2794 
2795 	if (IV_NULL == voucher) {
2796 		return KERN_INVALID_ARGUMENT;
2797 	}
2798 
2799 	/* Setup a recipe to copy all attributes. */
2800 	if (recipe_size < sizeof(*recipe)) {
2801 		return KERN_NO_SPACE;
2802 	}
2803 
2804 	*need_processing = FALSE;
2805 	recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2806 	recipe->key = MACH_VOUCHER_ATTR_KEY_ALL;
2807 	recipe->command = MACH_VOUCHER_ATTR_COPY;
2808 	recipe->previous_voucher = voucher;
2809 	recipe->content_size = 0;
2810 	recipe_used += sizeof(*recipe) + recipe->content_size;
2811 
2812 	for (key_index = 0; key_index < voucher->iv_table_size; key_index++) {
2813 		ipc_voucher_attr_manager_t manager;
2814 		mach_voucher_attr_key_t key;
2815 		iv_index_t value_index;
2816 
2817 		/* don't output anything for a default value */
2818 		value_index = iv_lookup(voucher, key_index);
2819 		if (IV_UNUSED_VALINDEX == value_index) {
2820 			continue;
2821 		}
2822 
2823 		if (recipe_size - recipe_used < sizeof(*recipe)) {
2824 			return KERN_NO_SPACE;
2825 		}
2826 
2827 		recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2828 
2829 		/*
2830 		 * Get the manager for this key_index. The
2831 		 * existence of a non-default value for this
2832 		 * slot within our voucher will keep the
2833 		 * manager referenced during the callout.
2834 		 */
2835 		ivgt_lookup(key_index, FALSE, &manager, NULL);
2836 		assert(IVAM_NULL != manager);
2837 		if (IVAM_NULL == manager) {
2838 			continue;
2839 		}
2840 
2841 		/* Check if the supported flag is set in the manager */
2842 		if ((manager->ivam_flags & flags) == 0) {
2843 			continue;
2844 		}
2845 
2846 		key = iv_index_to_key(key_index);
2847 
2848 		recipe->key = key;
2849 		recipe->command = command;
2850 		recipe->content_size = 0;
2851 		recipe->previous_voucher = voucher;
2852 
2853 		recipe_used += sizeof(*recipe) + recipe->content_size;
2854 		*need_processing = TRUE;
2855 	}
2856 
2857 	*in_out_size = recipe_used;
2858 	return KERN_SUCCESS;
2859 }
2860 
2861 /*
2862  * Activity id Generation
2863  */
2864 uint64_t voucher_activity_id;
2865 
2866 #define generate_activity_id(x) \
2867 	((uint64_t)OSAddAtomic64((x), (int64_t *)&voucher_activity_id))
2868 
2869 /*
2870  *	Routine:	mach_init_activity_id
2871  *	Purpose:
2872  *		Initialize voucher activity id.
2873  */
2874 void
mach_init_activity_id(void)2875 mach_init_activity_id(void)
2876 {
2877 	voucher_activity_id = 1;
2878 }
2879 
2880 /*
2881  *	Routine:	mach_generate_activity_id
2882  *	Purpose:
2883  *		Generate a system wide voucher activity id.
2884  */
2885 kern_return_t
mach_generate_activity_id(struct mach_generate_activity_id_args * args)2886 mach_generate_activity_id(
2887 	struct mach_generate_activity_id_args *args)
2888 {
2889 	uint64_t activity_id;
2890 	kern_return_t kr = KERN_SUCCESS;
2891 
2892 	if (args->count <= 0 || args->count > MACH_ACTIVITY_ID_COUNT_MAX) {
2893 		return KERN_INVALID_ARGUMENT;
2894 	}
2895 
2896 	activity_id = generate_activity_id(args->count);
2897 	kr = copyout(&activity_id, args->activity_id, sizeof(activity_id));
2898 
2899 	return kr;
2900 }
2901 
2902 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2903 
2904 /*
2905  * Build-in a simple User Data Resource Manager
2906  */
2907 #define USER_DATA_MAX_DATA      (16*1024)
2908 
2909 struct user_data_value_element {
2910 	mach_voucher_attr_value_reference_t     e_made;
2911 	mach_voucher_attr_content_size_t        e_size;
2912 	iv_index_t                              e_sum;
2913 	iv_index_t                              e_hash;
2914 	queue_chain_t                           e_hash_link;
2915 	uint8_t                                *e_data;
2916 };
2917 
2918 typedef struct user_data_value_element *user_data_element_t;
2919 
2920 /*
2921  * User Data Voucher Hash Table
2922  */
2923 #define USER_DATA_HASH_BUCKETS 127
2924 #define USER_DATA_HASH_BUCKET(x) ((x) % USER_DATA_HASH_BUCKETS)
2925 
2926 static queue_head_t user_data_bucket[USER_DATA_HASH_BUCKETS];
2927 static LCK_SPIN_DECLARE_ATTR(user_data_lock_data, &ipc_lck_grp, &ipc_lck_attr);
2928 
2929 #define user_data_lock_destroy() \
2930 	lck_spin_destroy(&user_data_lock_data, &ipc_lck_grp)
2931 #define user_data_lock() \
2932 	lck_spin_lock_grp(&user_data_lock_data, &ipc_lck_grp)
2933 #define user_data_lock_try() \
2934 	lck_spin_try_lock_grp(&user_data_lock_data, &ipc_lck_grp)
2935 #define user_data_unlock() \
2936 	lck_spin_unlock(&user_data_lock_data)
2937 
2938 static kern_return_t
2939 user_data_release_value(
2940 	ipc_voucher_attr_manager_t              manager,
2941 	mach_voucher_attr_key_t                 key,
2942 	mach_voucher_attr_value_handle_t        value,
2943 	mach_voucher_attr_value_reference_t     sync);
2944 
2945 static kern_return_t
2946 user_data_get_value(
2947 	ipc_voucher_attr_manager_t                      manager,
2948 	mach_voucher_attr_key_t                         key,
2949 	mach_voucher_attr_recipe_command_t              command,
2950 	mach_voucher_attr_value_handle_array_t          prev_values,
2951 	mach_voucher_attr_value_handle_array_size_t     prev_value_count,
2952 	mach_voucher_attr_content_t                     content,
2953 	mach_voucher_attr_content_size_t                content_size,
2954 	mach_voucher_attr_value_handle_t                *out_value,
2955 	mach_voucher_attr_value_flags_t                 *out_flags,
2956 	ipc_voucher_t                                   *out_value_voucher);
2957 
2958 static kern_return_t
2959 user_data_extract_content(
2960 	ipc_voucher_attr_manager_t                      manager,
2961 	mach_voucher_attr_key_t                         key,
2962 	mach_voucher_attr_value_handle_array_t          values,
2963 	mach_voucher_attr_value_handle_array_size_t     value_count,
2964 	mach_voucher_attr_recipe_command_t              *out_command,
2965 	mach_voucher_attr_content_t                     out_content,
2966 	mach_voucher_attr_content_size_t                *in_out_content_size);
2967 
2968 static kern_return_t
2969 user_data_command(
2970 	ipc_voucher_attr_manager_t                              manager,
2971 	mach_voucher_attr_key_t                                 key,
2972 	mach_voucher_attr_value_handle_array_t  values,
2973 	mach_msg_type_number_t                                  value_count,
2974 	mach_voucher_attr_command_t                             command,
2975 	mach_voucher_attr_content_t                             in_content,
2976 	mach_voucher_attr_content_size_t                in_content_size,
2977 	mach_voucher_attr_content_t                             out_content,
2978 	mach_voucher_attr_content_size_t                *out_content_size);
2979 
2980 static void
2981 user_data_release(
2982 	ipc_voucher_attr_manager_t              manager);
2983 
2984 const struct ipc_voucher_attr_manager user_data_manager = {
2985 	.ivam_release_value =   user_data_release_value,
2986 	.ivam_get_value =       user_data_get_value,
2987 	.ivam_extract_content = user_data_extract_content,
2988 	.ivam_command =         user_data_command,
2989 	.ivam_release =         user_data_release,
2990 	.ivam_flags =           IVAM_FLAGS_NONE,
2991 };
2992 
2993 ipc_voucher_attr_control_t user_data_control;
2994 ipc_voucher_attr_control_t test_control;
2995 
2996 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) && defined(MACH_VOUCHER_ATTR_KEY_TEST)
2997 #define USER_DATA_ASSERT_KEY(key)                               \
2998 	assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key) ||      \
2999 	       MACH_VOUCHER_ATTR_KEY_TEST == (key));
3000 #elif defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
3001 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key))
3002 #else
3003 #define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_TEST == (key))
3004 #endif
3005 
3006 static void
user_data_value_element_free(user_data_element_t elem)3007 user_data_value_element_free(user_data_element_t elem)
3008 {
3009 	kfree_data(elem->e_data, elem->e_size);
3010 	kfree_type(struct user_data_value_element, elem);
3011 }
3012 
3013 /*
3014  *	Routine:	user_data_release_value
3015  *	Purpose:
3016  *		Release a made reference on a specific value managed by
3017  *		this voucher attribute manager.
3018  *	Conditions:
3019  *		Must remove the element associated with this value from
3020  *		the hash if this is the last know made reference.
3021  */
3022 static kern_return_t
user_data_release_value(ipc_voucher_attr_manager_t __assert_only manager,mach_voucher_attr_key_t __assert_only key,mach_voucher_attr_value_handle_t value,mach_voucher_attr_value_reference_t sync)3023 user_data_release_value(
3024 	ipc_voucher_attr_manager_t              __assert_only manager,
3025 	mach_voucher_attr_key_t                 __assert_only key,
3026 	mach_voucher_attr_value_handle_t        value,
3027 	mach_voucher_attr_value_reference_t     sync)
3028 {
3029 	user_data_element_t elem;
3030 	iv_index_t hash;
3031 
3032 	assert(&user_data_manager == manager);
3033 	USER_DATA_ASSERT_KEY(key);
3034 
3035 	elem = (user_data_element_t)value;
3036 	hash = elem->e_hash;
3037 
3038 	user_data_lock();
3039 	if (sync == elem->e_made) {
3040 		queue_remove(&user_data_bucket[hash], elem, user_data_element_t, e_hash_link);
3041 		user_data_unlock();
3042 		user_data_value_element_free(elem);
3043 		return KERN_SUCCESS;
3044 	}
3045 	assert(sync < elem->e_made);
3046 	user_data_unlock();
3047 
3048 	return KERN_FAILURE;
3049 }
3050 
3051 /*
3052  *	Routine:	user_data_checksum
3053  *	Purpose:
3054  *		Provide a rudimentary checksum for the data presented
3055  *		to these voucher attribute managers.
3056  */
3057 static iv_index_t
user_data_checksum(mach_voucher_attr_content_t content,mach_voucher_attr_content_size_t content_size)3058 user_data_checksum(
3059 	mach_voucher_attr_content_t                     content,
3060 	mach_voucher_attr_content_size_t                content_size)
3061 {
3062 	mach_voucher_attr_content_size_t i;
3063 	iv_index_t cksum = 0;
3064 
3065 	for (i = 0; i < content_size; i++, content++) {
3066 		cksum = (cksum << 8) ^ (cksum + *(unsigned char *)content);
3067 	}
3068 
3069 	return ~cksum;
3070 }
3071 
3072 /*
3073  *	Routine:	user_data_dedup
3074  *	Purpose:
3075  *		See if the content represented by this request already exists
3076  *		in another user data element.  If so return a made reference
3077  *		to the existing element.  Otherwise, create a new element and
3078  *		return that (after inserting it in the hash).
3079  *	Conditions:
3080  *		Nothing locked.
3081  *	Returns:
3082  *		A made reference on the user_data_element_t
3083  */
3084 static user_data_element_t
user_data_dedup(mach_voucher_attr_content_t content,mach_voucher_attr_content_size_t content_size)3085 user_data_dedup(
3086 	mach_voucher_attr_content_t                     content,
3087 	mach_voucher_attr_content_size_t                content_size)
3088 {
3089 	iv_index_t sum;
3090 	iv_index_t hash;
3091 	user_data_element_t elem;
3092 	user_data_element_t alloc = NULL;
3093 
3094 	sum = user_data_checksum(content, content_size);
3095 	hash = USER_DATA_HASH_BUCKET(sum);
3096 
3097 retry:
3098 	user_data_lock();
3099 	queue_iterate(&user_data_bucket[hash], elem, user_data_element_t, e_hash_link) {
3100 		assert(elem->e_hash == hash);
3101 
3102 		/* if sums match... */
3103 		if (elem->e_sum == sum && elem->e_size == content_size) {
3104 			iv_index_t i;
3105 
3106 			/* and all data matches */
3107 			for (i = 0; i < content_size; i++) {
3108 				if (elem->e_data[i] != content[i]) {
3109 					break;
3110 				}
3111 			}
3112 			if (i < content_size) {
3113 				continue;
3114 			}
3115 
3116 			/* ... we found a match... */
3117 
3118 			elem->e_made++;
3119 			user_data_unlock();
3120 
3121 			if (NULL != alloc) {
3122 				user_data_value_element_free(alloc);
3123 			}
3124 
3125 			return elem;
3126 		}
3127 	}
3128 
3129 	if (NULL == alloc) {
3130 		user_data_unlock();
3131 
3132 		alloc = kalloc_type(struct user_data_value_element,
3133 		    Z_WAITOK | Z_NOFAIL);
3134 		alloc->e_made = 1;
3135 		alloc->e_size = content_size;
3136 		alloc->e_sum = sum;
3137 		alloc->e_hash = hash;
3138 		alloc->e_data = kalloc_data(content_size, Z_WAITOK | Z_NOFAIL);
3139 		memcpy(alloc->e_data, content, content_size);
3140 		goto retry;
3141 	}
3142 
3143 	queue_enter(&user_data_bucket[hash], alloc, user_data_element_t, e_hash_link);
3144 	user_data_unlock();
3145 
3146 	return alloc;
3147 }
3148 
3149 static kern_return_t
user_data_get_value(ipc_voucher_attr_manager_t __assert_only manager,mach_voucher_attr_key_t __assert_only key,mach_voucher_attr_recipe_command_t command,mach_voucher_attr_value_handle_array_t prev_values,mach_voucher_attr_value_handle_array_size_t prev_value_count,mach_voucher_attr_content_t content,mach_voucher_attr_content_size_t content_size,mach_voucher_attr_value_handle_t * out_value,mach_voucher_attr_value_flags_t * out_flags,ipc_voucher_t * out_value_voucher)3150 user_data_get_value(
3151 	ipc_voucher_attr_manager_t                      __assert_only manager,
3152 	mach_voucher_attr_key_t                         __assert_only key,
3153 	mach_voucher_attr_recipe_command_t              command,
3154 	mach_voucher_attr_value_handle_array_t          prev_values,
3155 	mach_voucher_attr_value_handle_array_size_t     prev_value_count,
3156 	mach_voucher_attr_content_t                     content,
3157 	mach_voucher_attr_content_size_t                content_size,
3158 	mach_voucher_attr_value_handle_t                *out_value,
3159 	mach_voucher_attr_value_flags_t                 *out_flags,
3160 	ipc_voucher_t                                   *out_value_voucher)
3161 {
3162 	user_data_element_t elem;
3163 
3164 	assert(&user_data_manager == manager);
3165 	USER_DATA_ASSERT_KEY(key);
3166 
3167 	/* never an out voucher */
3168 	*out_value_voucher = IPC_VOUCHER_NULL;
3169 	*out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
3170 
3171 	switch (command) {
3172 	case MACH_VOUCHER_ATTR_REDEEM:
3173 
3174 		/* redeem of previous values is the value */
3175 		if (0 < prev_value_count) {
3176 			elem = (user_data_element_t)prev_values[0];
3177 
3178 			user_data_lock();
3179 			assert(0 < elem->e_made);
3180 			elem->e_made++;
3181 			user_data_unlock();
3182 
3183 			*out_value = (mach_voucher_attr_value_handle_t)elem;
3184 			return KERN_SUCCESS;
3185 		}
3186 
3187 		/* redeem of default is default */
3188 		*out_value = 0;
3189 		return KERN_SUCCESS;
3190 
3191 	case MACH_VOUCHER_ATTR_USER_DATA_STORE:
3192 		if (USER_DATA_MAX_DATA < content_size) {
3193 			return KERN_RESOURCE_SHORTAGE;
3194 		}
3195 
3196 		/* empty is the default */
3197 		if (0 == content_size) {
3198 			*out_value = 0;
3199 			return KERN_SUCCESS;
3200 		}
3201 
3202 		elem = user_data_dedup(content, content_size);
3203 		*out_value = (mach_voucher_attr_value_handle_t)elem;
3204 		return KERN_SUCCESS;
3205 
3206 	default:
3207 		/* every other command is unknown */
3208 		return KERN_INVALID_ARGUMENT;
3209 	}
3210 }
3211 
3212 static kern_return_t
user_data_extract_content(ipc_voucher_attr_manager_t __assert_only manager,mach_voucher_attr_key_t __assert_only key,mach_voucher_attr_value_handle_array_t values,mach_voucher_attr_value_handle_array_size_t value_count,mach_voucher_attr_recipe_command_t * out_command,mach_voucher_attr_content_t out_content,mach_voucher_attr_content_size_t * in_out_content_size)3213 user_data_extract_content(
3214 	ipc_voucher_attr_manager_t                      __assert_only manager,
3215 	mach_voucher_attr_key_t                         __assert_only key,
3216 	mach_voucher_attr_value_handle_array_t          values,
3217 	mach_voucher_attr_value_handle_array_size_t     value_count,
3218 	mach_voucher_attr_recipe_command_t              *out_command,
3219 	mach_voucher_attr_content_t                     out_content,
3220 	mach_voucher_attr_content_size_t                *in_out_content_size)
3221 {
3222 	mach_voucher_attr_content_size_t size = 0;
3223 	user_data_element_t elem;
3224 	unsigned int i;
3225 
3226 	assert(&user_data_manager == manager);
3227 	USER_DATA_ASSERT_KEY(key);
3228 
3229 	/* concatenate the stored data items */
3230 	for (i = 0; i < value_count && *in_out_content_size > 0; i++) {
3231 		elem = (user_data_element_t)values[i];
3232 		assert(USER_DATA_MAX_DATA >= elem->e_size);
3233 
3234 		if (size + elem->e_size > *in_out_content_size) {
3235 			return KERN_NO_SPACE;
3236 		}
3237 
3238 		memcpy(&out_content[size], elem->e_data, elem->e_size);
3239 		size += elem->e_size;
3240 	}
3241 	*out_command = MACH_VOUCHER_ATTR_BITS_STORE;
3242 	*in_out_content_size = size;
3243 	return KERN_SUCCESS;
3244 }
3245 
3246 static kern_return_t
user_data_command(ipc_voucher_attr_manager_t __assert_only manager,mach_voucher_attr_key_t __assert_only key,mach_voucher_attr_value_handle_array_t __unused values,mach_msg_type_number_t __unused value_count,mach_voucher_attr_command_t __unused command,mach_voucher_attr_content_t __unused in_content,mach_voucher_attr_content_size_t __unused in_content_size,mach_voucher_attr_content_t __unused out_content,mach_voucher_attr_content_size_t __unused * out_content_size)3247 user_data_command(
3248 	ipc_voucher_attr_manager_t                              __assert_only manager,
3249 	mach_voucher_attr_key_t                                 __assert_only key,
3250 	mach_voucher_attr_value_handle_array_t  __unused values,
3251 	mach_msg_type_number_t                                  __unused value_count,
3252 	mach_voucher_attr_command_t                             __unused command,
3253 	mach_voucher_attr_content_t                             __unused in_content,
3254 	mach_voucher_attr_content_size_t                __unused in_content_size,
3255 	mach_voucher_attr_content_t                             __unused out_content,
3256 	mach_voucher_attr_content_size_t                __unused *out_content_size)
3257 {
3258 	assert(&user_data_manager == manager);
3259 	USER_DATA_ASSERT_KEY(key);
3260 	return KERN_FAILURE;
3261 }
3262 
3263 static void
user_data_release(ipc_voucher_attr_manager_t manager)3264 user_data_release(
3265 	ipc_voucher_attr_manager_t              manager)
3266 {
3267 	if (manager != &user_data_manager) {
3268 		return;
3269 	}
3270 
3271 	panic("Voucher user-data manager released");
3272 }
3273 
3274 __startup_func
3275 static void
user_data_attr_manager_init(void)3276 user_data_attr_manager_init(void)
3277 {
3278 	kern_return_t kr;
3279 
3280 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
3281 	kr = ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager,
3282 	    (mach_voucher_attr_value_handle_t)0,
3283 	    MACH_VOUCHER_ATTR_KEY_USER_DATA,
3284 	    &user_data_control);
3285 	if (KERN_SUCCESS != kr) {
3286 		printf("Voucher user-data manager register(USER-DATA) returned %d", kr);
3287 	}
3288 #endif
3289 #if defined(MACH_VOUCHER_ATTR_KEY_TEST)
3290 	kr = ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager,
3291 	    (mach_voucher_attr_value_handle_t)0,
3292 	    MACH_VOUCHER_ATTR_KEY_TEST,
3293 	    &test_control);
3294 	if (KERN_SUCCESS != kr) {
3295 		printf("Voucher user-data manager register(TEST) returned %d", kr);
3296 	}
3297 #endif
3298 #if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
3299 	for (int i = 0; i < USER_DATA_HASH_BUCKETS; i++) {
3300 		queue_init(&user_data_bucket[i]);
3301 	}
3302 #endif
3303 }
3304 STARTUP(MACH_IPC, STARTUP_RANK_FIRST, user_data_attr_manager_init);
3305 
3306 #endif /* MACH_VOUCHER_ATTR_KEY_USER_DATA || MACH_VOUCHER_ATTR_KEY_TEST */
3307