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