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