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