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