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