]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ipc/ipc_voucher.c
xnu-3247.10.11.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_voucher.c
CommitLineData
fe8ab488
A
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");
3e170ce0 827 return;
fe8ab488
A
828 }
829
830 old_size = ivac->ivac_table_size;
831 ivac_unlock(ivac);
832
3e170ce0 833 new_size = old_size * 2;
fe8ab488
A
834
835 assert(new_size > old_size);
3e170ce0
A
836 assert(new_size < IVAC_ENTRIES_MAX);
837
fe8ab488
A
838 new_table = kalloc(sizeof(ivac_entry) * new_size);
839 if (!new_table){
840 panic("Failed to grow ivac table to size %d\n", new_size);
841 return;
842 }
843
844 /* setup the free list for new entries */
845 for (i = old_size; i < new_size; i++) {
846 ivace_reset_data(&new_table[i], i+1);
847 }
848
849 ivac_lock(ivac);
850
851 for (i = 0; i < ivac->ivac_table_size; i++){
852 ivace_copy_data(&ivac->ivac_table[i], &new_table[i]);
853 }
854
855 old_table = ivac->ivac_table;
856
857 ivac->ivac_table = new_table;
858 ivac->ivac_table_size = new_size;
859
860 /* adding new free entries at head of freelist */
861 ivac->ivac_table[new_size - 1].ivace_next = ivac->ivac_freelist;
862 ivac->ivac_freelist = old_size;
863 ivac->ivac_is_growing = 0;
864 ivac_wakeup(ivac);
865
866 if (old_table){
867 ivac_unlock(ivac);
868 kfree(old_table, old_size * sizeof(ivac_entry));
869 ivac_lock(ivac);
870 }
871}
872
873/*
874 * ivace_reference_by_index
875 *
876 * Take an additional reference on the <key_index, val_index>
877 * cached value. It is assumed the caller already holds a
878 * reference to the same cached key-value pair.
879 */
880static void
881ivace_reference_by_index(
882 iv_index_t key_index,
883 iv_index_t val_index)
884{
885 ipc_voucher_attr_control_t ivac;
886 ivac_entry_t ivace;
887
888 if (IV_UNUSED_VALINDEX == val_index)
889 return;
890
891 ivgt_lookup(key_index, FALSE, NULL, &ivac);
892 assert(IVAC_NULL != ivac);
893
894 ivac_lock(ivac);
895 assert(val_index < ivac->ivac_table_size);
896 ivace = &ivac->ivac_table[val_index];
897
898 assert(0xdeadc0dedeadc0de != ivace->ivace_value);
899 assert(0 < ivace->ivace_refs);
900 assert(!ivace->ivace_free);
901 ivace->ivace_refs++;
902 ivac_unlock(ivac);
903}
904
905
906/*
907 * Look up the values for a given <key, index> pair.
908 *
909 * Consumes a reference on the passed voucher control.
910 * Either it is donated to a newly-created value cache
911 * or it is released (if we piggy back on an existing
912 * value cache entry).
913 */
914static iv_index_t
915ivace_reference_by_value(
916 ipc_voucher_attr_control_t ivac,
917 mach_voucher_attr_value_handle_t value)
918{
919 ivac_entry_t ivace = IVACE_NULL;
920 iv_index_t hash_index;
921 iv_index_t index;
922
923 if (IVAC_NULL == ivac) {
924 return IV_UNUSED_VALINDEX;
925 }
926
927 ivac_lock(ivac);
928restart:
929 hash_index = IV_HASH_VAL(ivac->ivac_init_table_size, value);
930 index = ivac->ivac_table[hash_index].ivace_index;
931 while (index != IV_HASH_END) {
932 assert(index < ivac->ivac_table_size);
933 ivace = &ivac->ivac_table[index];
934 assert(!ivace->ivace_free);
935
936 if (ivace->ivace_value == value)
937 break;
938
939 assert(ivace->ivace_next != index);
940 index = ivace->ivace_next;
941 }
942
943 /* found it? */
944 if (index != IV_HASH_END) {
945 /* only add reference on non-default value */
946 if (IV_UNUSED_VALINDEX != index) {
947 ivace->ivace_refs++;
948 ivace->ivace_made++;
949 }
950
951 ivac_unlock(ivac);
952 ivac_release(ivac);
953 return index;
954 }
955
956 /* insert new entry in the table */
957 index = ivac->ivac_freelist;
958 if (IV_FREELIST_END == index) {
959 /* freelist empty */
960 ivac_grow_table(ivac);
961 goto restart;
962 }
963
964 /* take the entry off the freelist */
965 ivace = &ivac->ivac_table[index];
966 ivac->ivac_freelist = ivace->ivace_next;
967
968 /* initialize the new entry */
969 ivace->ivace_value = value;
970 ivace->ivace_refs = 1;
971 ivace->ivace_made = 1;
972 ivace->ivace_free = FALSE;
973
974 /* insert the new entry in the proper hash chain */
975 ivace->ivace_next = ivac->ivac_table[hash_index].ivace_index;
976 ivac->ivac_table[hash_index].ivace_index = index;
977 ivac_unlock(ivac);
978
979 /* donated passed in ivac reference to new entry */
980
981 return index;
982}
983
984/*
985 * Release a reference on the given <key_index, value_index> pair.
986 *
987 * Conditions: called with nothing locked, as it may cause
988 * callouts and/or messaging to the resource
989 * manager.
990 */
991static void ivace_release(
992 iv_index_t key_index,
993 iv_index_t value_index)
994{
995 ipc_voucher_attr_control_t ivac;
996 ipc_voucher_attr_manager_t ivam;
997 mach_voucher_attr_value_handle_t value;
998 mach_voucher_attr_value_reference_t made;
999 mach_voucher_attr_key_t key;
1000 iv_index_t hash_index;
1001 ivac_entry_t ivace;
1002 kern_return_t kr;
1003
1004 /* cant release the default value */
1005 if (IV_UNUSED_VALINDEX == value_index)
1006 return;
1007
1008 ivgt_lookup(key_index, FALSE, &ivam, &ivac);
1009 assert(IVAC_NULL != ivac);
1010 assert(IVAM_NULL != ivam);
1011
1012 ivac_lock(ivac);
1013 assert(value_index < ivac->ivac_table_size);
1014 ivace = &ivac->ivac_table[value_index];
1015
1016 assert(0 < ivace->ivace_refs);
1017
1018 if (0 < --ivace->ivace_refs) {
1019 ivac_unlock(ivac);
1020 return;
1021 }
1022
1023 key = iv_index_to_key(key_index);
1024 assert(MACH_VOUCHER_ATTR_KEY_NONE != key);
1025
1026 /*
1027 * if last return reply is still pending,
1028 * let it handle this later return when
1029 * the previous reply comes in.
1030 */
1031 if (ivace->ivace_releasing) {
1032 ivac_unlock(ivac);
1033 return;
1034 }
1035
1036 /* claim releasing */
1037 ivace->ivace_releasing = TRUE;
1038 value = ivace->ivace_value;
1039
1040 redrive:
1041 assert(value == ivace->ivace_value);
1042 assert(!ivace->ivace_free);
1043 made = ivace->ivace_made;
1044 ivac_unlock(ivac);
1045
1046 /* callout to manager's release_value */
1047 kr = (ivam->ivam_release_value)(ivam, key, value, made);
1048
1049 /* recalculate entry address as table may have changed */
1050 ivac_lock(ivac);
1051 ivace = &ivac->ivac_table[value_index];
1052 assert(value == ivace->ivace_value);
1053
1054 /*
1055 * new made values raced with this return. If the
1056 * manager OK'ed the prior release, we have to start
1057 * the made numbering over again (pretend the race
1058 * didn't happen). If the entry has zero refs again,
1059 * re-drive the release.
1060 */
1061 if (ivace->ivace_made != made) {
1062 assert(made < ivace->ivace_made);
1063
1064 if (KERN_SUCCESS == kr)
1065 ivace->ivace_made -= made;
1066
1067 if (0 == ivace->ivace_refs)
1068 goto redrive;
1069
1070 ivace->ivace_releasing = FALSE;
1071 ivac_unlock(ivac);
1072 return;
1073 } else {
1074 /*
1075 * If the manager returned FAILURE, someone took a
1076 * reference on the value but have not updated the ivace,
1077 * release the lock and return since thread who got
1078 * the new reference will update the ivace and will have
1079 * non-zero reference on the value.
1080 */
1081 if (KERN_SUCCESS != kr) {
1082 ivace->ivace_releasing = FALSE;
1083 ivac_unlock(ivac);
1084 return;
1085 }
1086 }
1087
1088 assert(0 == ivace->ivace_refs);
1089
1090 /*
1091 * going away - remove entry from its hash
1092 * If its at the head of the hash bucket list (common), unchain
1093 * at the head. Otherwise walk the chain until the next points
1094 * at this entry, and remove it from the the list there.
1095 */
1096 hash_index = iv_hash_value(key_index, value);
1097 if (ivac->ivac_table[hash_index].ivace_index == value_index) {
1098 ivac->ivac_table[hash_index].ivace_index = ivace->ivace_next;
1099 } else {
1100 hash_index = ivac->ivac_table[hash_index].ivace_index;
1101 assert(IV_HASH_END != hash_index);
1102 while (ivac->ivac_table[hash_index].ivace_next != value_index) {
1103 hash_index = ivac->ivac_table[hash_index].ivace_next;
1104 assert(IV_HASH_END != hash_index);
1105 }
1106 ivac->ivac_table[hash_index].ivace_next = ivace->ivace_next;
1107 }
1108
1109 /* Put this entry on the freelist */
1110 ivace->ivace_value = 0xdeadc0dedeadc0de;
1111 ivace->ivace_releasing = FALSE;
1112 ivace->ivace_free = TRUE;
1113 ivace->ivace_made = 0;
1114 ivace->ivace_next = ivac->ivac_freelist;
1115 ivac->ivac_freelist = value_index;
1116 ivac_unlock(ivac);
1117
1118 /* release the reference this value held on its cache control */
1119 ivac_release(ivac);
1120
1121 return;
1122}
1123
1124
1125/*
1126 * ivgt_looup
1127 *
1128 * Lookup an entry in the global table from the context of a manager
1129 * registration. Adds a reference to the control to keep the results
1130 * around (if needed).
1131 *
1132 * Because of the calling point, we can't be sure the manager is
1133 * [fully] registered yet. So, we must hold the global table lock
1134 * during the lookup to synchronize with in-parallel registrations
1135 * (and possible table growth).
1136 */
1137static void
1138ivgt_lookup(iv_index_t key_index,
1139 boolean_t take_reference,
1140 ipc_voucher_attr_manager_t *manager,
1141 ipc_voucher_attr_control_t *control)
1142{
1143 ipc_voucher_attr_control_t ivac;
1144
1145 if (key_index < MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN) {
1146 ivgt_lock();
1147 if (NULL != manager)
1148 *manager = iv_global_table[key_index].ivgte_manager;
1149 ivac = iv_global_table[key_index].ivgte_control;
1150 if (IVAC_NULL != ivac) {
1151 assert(key_index == ivac->ivac_key_index);
1152 if (take_reference) {
1153 assert(NULL != control);
1154 ivac_reference(ivac);
1155 }
1156 }
1157 ivgt_unlock();
1158 if (NULL != control)
1159 *control = ivac;
1160 } else {
1161 if (NULL != manager)
1162 *manager = IVAM_NULL;
1163 if (NULL != control)
1164 *control = IVAC_NULL;
1165 }
1166}
1167
1168/*
1169 * Routine: ipc_replace_voucher_value
1170 * Purpose:
1171 * Replace the <voucher, key> value with the results of
1172 * running the supplied command through the resource
1173 * manager's get-value callback.
1174 * Conditions:
1175 * Nothing locked (may invoke user-space repeatedly).
1176 * Caller holds references on voucher and previous voucher.
1177 */
1178static kern_return_t
1179ipc_replace_voucher_value(
1180 ipc_voucher_t voucher,
1181 mach_voucher_attr_key_t key,
1182 mach_voucher_attr_recipe_command_t command,
1183 ipc_voucher_t prev_voucher,
1184 mach_voucher_attr_content_t content,
1185 mach_voucher_attr_content_size_t content_size)
1186{
1187 mach_voucher_attr_value_handle_t previous_vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
1188 mach_voucher_attr_value_handle_array_size_t previous_vals_count;
1189 mach_voucher_attr_value_handle_t new_value;
1190 ipc_voucher_t new_value_voucher;
1191 ipc_voucher_attr_manager_t ivam;
1192 ipc_voucher_attr_control_t ivac;
1193 iv_index_t prev_val_index;
1194 iv_index_t save_val_index;
1195 iv_index_t val_index;
1196 iv_index_t key_index;
1197 kern_return_t kr;
1198
1199 /*
1200 * Get the manager for this key_index.
1201 * Returns a reference on the control.
1202 */
1203 key_index = iv_key_to_index(key);
1204 ivgt_lookup(key_index, TRUE, &ivam, &ivac);
1205 if (IVAM_NULL == ivam)
1206 return KERN_INVALID_ARGUMENT;
1207
1208 /* save the current value stored in the forming voucher */
1209 save_val_index = iv_lookup(voucher, key_index);
1210
1211 /*
1212 * Get the previous value(s) for this key creation.
1213 * If a previous voucher is specified, they come from there.
1214 * Otherwise, they come from the intermediate values already
1215 * in the forming voucher.
1216 */
1217 prev_val_index = (IV_NULL != prev_voucher) ?
1218 iv_lookup(prev_voucher, key_index) :
1219 save_val_index;
1220 ivace_lookup_values(key_index, prev_val_index,
1221 previous_vals, &previous_vals_count);
1222
1223 /* Call out to resource manager to get new value */
1224 new_value_voucher = IV_NULL;
1225 kr = (ivam->ivam_get_value)(
1226 ivam, key, command,
1227 previous_vals, previous_vals_count,
1228 content, content_size,
1229 &new_value, &new_value_voucher);
1230 if (KERN_SUCCESS != kr) {
1231 ivac_release(ivac);
1232 return kr;
1233 }
1234
1235 /* TODO: value insertion from returned voucher */
1236 if (IV_NULL != new_value_voucher)
1237 iv_release(new_value_voucher);
1238
1239 /*
1240 * Find or create a slot in the table associated
1241 * with this attribute value. The ivac reference
1242 * is transferred to a new value, or consumed if
1243 * we find a matching existing value.
1244 */
1245 val_index = ivace_reference_by_value(ivac, new_value);
1246 iv_set(voucher, key_index, val_index);
1247
1248 /*
1249 * release saved old value from the newly forming voucher
1250 * This is saved until the end to avoid churning the
1251 * release logic in cases where the same value is returned
1252 * as was there before.
1253 */
1254 ivace_release(key_index, save_val_index);
1255
1256 return KERN_SUCCESS;
1257}
1258
1259/*
1260 * Routine: ipc_directly_replace_voucher_value
1261 * Purpose:
1262 * Replace the <voucher, key> value with the value-handle
1263 * supplied directly by the attribute manager.
1264 * Conditions:
1265 * Nothing locked.
1266 * Caller holds references on voucher.
1267 * A made reference to the value-handle is donated by the caller.
1268 */
1269static kern_return_t
1270ipc_directly_replace_voucher_value(
1271 ipc_voucher_t voucher,
1272 mach_voucher_attr_key_t key,
1273 mach_voucher_attr_value_handle_t new_value)
1274{
1275 ipc_voucher_attr_manager_t ivam;
1276 ipc_voucher_attr_control_t ivac;
1277 iv_index_t save_val_index;
1278 iv_index_t val_index;
1279 iv_index_t key_index;
1280
1281 /*
1282 * Get the manager for this key_index.
1283 * Returns a reference on the control.
1284 */
1285 key_index = iv_key_to_index(key);
1286 ivgt_lookup(key_index, TRUE, &ivam, &ivac);
1287 if (IVAM_NULL == ivam)
1288 return KERN_INVALID_ARGUMENT;
1289
1290 /* save the current value stored in the forming voucher */
1291 save_val_index = iv_lookup(voucher, key_index);
1292
1293 /*
1294 * Find or create a slot in the table associated
1295 * with this attribute value. The ivac reference
1296 * is transferred to a new value, or consumed if
1297 * we find a matching existing value.
1298 */
1299 val_index = ivace_reference_by_value(ivac, new_value);
1300 iv_set(voucher, key_index, val_index);
1301
1302 /*
1303 * release saved old value from the newly forming voucher
1304 * This is saved until the end to avoid churning the
1305 * release logic in cases where the same value is returned
1306 * as was there before.
1307 */
1308 ivace_release(key_index, save_val_index);
1309
1310 return KERN_SUCCESS;
1311}
1312
1313static kern_return_t
1314ipc_execute_voucher_recipe_command(
1315 ipc_voucher_t voucher,
1316 mach_voucher_attr_key_t key,
1317 mach_voucher_attr_recipe_command_t command,
1318 ipc_voucher_t prev_iv,
1319 mach_voucher_attr_content_t content,
1320 mach_voucher_attr_content_size_t content_size,
1321 boolean_t key_priv)
1322{
1323 iv_index_t prev_val_index;
1324 iv_index_t val_index;
1325 kern_return_t kr;
1326
1327 switch (command) {
1328
1329 /*
1330 * MACH_VOUCHER_ATTR_COPY
1331 * Copy the attribute(s) from the previous voucher to the new
1332 * one. A wildcard key is an acceptable value - indicating a
1333 * desire to copy all the attribute values from the previous
1334 * voucher.
1335 */
1336 case MACH_VOUCHER_ATTR_COPY:
1337
1338 /* no recipe data on a copy */
1339 if (0 < content_size)
1340 return KERN_INVALID_ARGUMENT;
1341
1342 /* nothing to copy from? - done */
1343 if (IV_NULL == prev_iv)
1344 return KERN_SUCCESS;
1345
1346 if (MACH_VOUCHER_ATTR_KEY_ALL == key) {
1347 iv_index_t limit, j;
1348
1349 /* reconcile possible difference in voucher sizes */
1350 limit = (prev_iv->iv_table_size < voucher->iv_table_size) ?
1351 prev_iv->iv_table_size :
1352 voucher->iv_table_size;
1353
1354 /* wildcard matching */
1355 for (j = 0; j < limit; j++) {
1356 /* release old value being replaced */
1357 val_index = iv_lookup(voucher, j);
1358 ivace_release(j, val_index);
1359
1360 /* replace with reference to prev voucher's value */
1361 prev_val_index = iv_lookup(prev_iv, j);
1362 ivace_reference_by_index(j, prev_val_index);
1363 iv_set(voucher, j, prev_val_index);
1364 }
1365 } else {
1366 iv_index_t key_index;
1367
1368 /* copy just one key */
1369 key_index = iv_key_to_index(key);
1370 if (ivgt_keys_in_use < key_index)
1371 return KERN_INVALID_ARGUMENT;
1372
1373 /* release old value being replaced */
1374 val_index = iv_lookup(voucher, key_index);
1375 ivace_release(key_index, val_index);
1376
1377 /* replace with reference to prev voucher's value */
1378 prev_val_index = iv_lookup(prev_iv, key_index);
1379 ivace_reference_by_index(key_index, prev_val_index);
1380 iv_set(voucher, key_index, prev_val_index);
1381 }
1382 break;
1383
1384 /*
1385 * MACH_VOUCHER_ATTR_REMOVE
1386 * Remove the attribute(s) from the under construction voucher.
1387 * A wildcard key is an acceptable value - indicating a desire
1388 * to remove all the attribute values set up so far in the voucher.
1389 * If a previous voucher is specified, only remove the value it
1390 * it matches the value in the previous voucher.
1391 */
1392 case MACH_VOUCHER_ATTR_REMOVE:
1393 /* no recipe data on a remove */
1394 if (0 < content_size)
1395 return KERN_INVALID_ARGUMENT;
1396
1397 if (MACH_VOUCHER_ATTR_KEY_ALL == key) {
1398 iv_index_t limit, j;
1399
1400 /* reconcile possible difference in voucher sizes */
1401 limit = (IV_NULL == prev_iv) ? voucher->iv_table_size :
1402 ((prev_iv->iv_table_size < voucher->iv_table_size) ?
1403 prev_iv->iv_table_size : voucher->iv_table_size);
1404
1405 /* wildcard matching */
1406 for (j = 0; j < limit; j++) {
1407 val_index = iv_lookup(voucher, j);
1408
1409 /* If not matched in previous, skip */
1410 if (IV_NULL != prev_iv) {
1411 prev_val_index = iv_lookup(prev_iv, j);
1412 if (val_index != prev_val_index)
1413 continue;
1414 }
1415 /* release and clear */
1416 ivace_release(j, val_index);
1417 iv_set(voucher, j, IV_UNUSED_VALINDEX);
1418 }
1419 } else {
1420 iv_index_t key_index;
1421
1422 /* copy just one key */
1423 key_index = iv_key_to_index(key);
1424 if (ivgt_keys_in_use < key_index)
1425 return KERN_INVALID_ARGUMENT;
1426
1427 val_index = iv_lookup(voucher, key_index);
1428
1429 /* If not matched in previous, skip */
1430 if (IV_NULL != prev_iv) {
1431 prev_val_index = iv_lookup(prev_iv, key_index);
1432 if (val_index != prev_val_index)
1433 break;
1434 }
1435
1436 /* release and clear */
1437 ivace_release(key_index, val_index);
1438 iv_set(voucher, key_index, IV_UNUSED_VALINDEX);
1439 }
1440 break;
1441
1442 /*
1443 * MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
1444 * Use key-privilege to set a value handle for the attribute directly,
1445 * rather than triggering a callback into the attribute manager to
1446 * interpret a recipe to generate the value handle.
1447 */
1448 case MACH_VOUCHER_ATTR_SET_VALUE_HANDLE:
1449 if (key_priv) {
1450 mach_voucher_attr_value_handle_t new_value;
1451
1452 if (sizeof(mach_voucher_attr_value_handle_t) != content_size)
1453 return KERN_INVALID_ARGUMENT;
1454
1455 new_value = *(mach_voucher_attr_value_handle_t *)(void *)content;
1456 kr = ipc_directly_replace_voucher_value(voucher,
1457 key,
1458 new_value);
1459 if (KERN_SUCCESS != kr)
1460 return kr;
1461 } else
1462 return KERN_INVALID_CAPABILITY;
1463 break;
1464
1465 /*
1466 * MACH_VOUCHER_ATTR_REDEEM
1467 * Redeem the attribute(s) from the previous voucher for a possibly
1468 * new value in the new voucher. A wildcard key is an acceptable value,
1469 * indicating a desire to redeem all the values.
1470 */
1471 case MACH_VOUCHER_ATTR_REDEEM:
1472
1473 if (MACH_VOUCHER_ATTR_KEY_ALL == key) {
1474 iv_index_t limit, j;
1475
1476 /* reconcile possible difference in voucher sizes */
1477 if (IV_NULL != prev_iv)
1478 limit = (prev_iv->iv_table_size < voucher->iv_table_size) ?
1479 prev_iv->iv_table_size :
1480 voucher->iv_table_size;
1481 else
1482 limit = voucher->iv_table_size;
1483
1484 /* wildcard matching */
1485 for (j = 0; j < limit; j++) {
1486 mach_voucher_attr_key_t j_key;
1487
1488 j_key = iv_index_to_key(j);
1489
1490 /* skip non-existent managers */
1491 if (MACH_VOUCHER_ATTR_KEY_NONE == j_key)
1492 continue;
1493
1494 /* get the new value from redeem (skip empty previous) */
1495 kr = ipc_replace_voucher_value(voucher,
1496 j_key,
1497 command,
1498 prev_iv,
1499 content,
1500 content_size);
1501 if (KERN_SUCCESS != kr)
1502 return kr;
1503 }
1504 break;
1505 }
1506 /* fall thru for single key redemption */
1507
1508 /*
1509 * DEFAULT:
1510 * Replace the current value for the <voucher, key> pair with whatever
1511 * value the resource manager returns for the command and recipe
1512 * combination provided.
1513 */
1514 default:
1515 kr = ipc_replace_voucher_value(voucher,
1516 key,
1517 command,
1518 prev_iv,
1519 content,
1520 content_size);
1521 if (KERN_SUCCESS != kr)
1522 return kr;
1523
1524 break;
1525 }
1526 return KERN_SUCCESS;
1527}
1528
1529/*
1530 * Routine: iv_checksum
1531 * Purpose:
1532 * Compute the voucher sum. This is more position-
1533 * relevant than many other checksums - important for
1534 * vouchers (arrays of low, oft-reused, indexes).
1535 */
1536static inline iv_index_t
1537iv_checksum(ipc_voucher_t voucher, boolean_t *emptyp)
1538{
1539 iv_index_t c = 0;
1540
1541 boolean_t empty = TRUE;
1542 if (0 < voucher->iv_table_size) {
1543 iv_index_t i = voucher->iv_table_size - 1;
1544
1545 do {
1546 iv_index_t v = voucher->iv_table[i];
1547 c = c << 3 | c >> (32 - 3); /* rotate */
1548 c = ~c; /* invert */
1549 if (0 < v) {
1550 c += v; /* add in */
1551 empty = FALSE;
1552 }
1553 } while (0 < i--);
1554 }
1555 *emptyp = empty;
1556 return c;
1557}
1558
1559/*
1560 * Routine: iv_dedup
1561 * Purpose:
1562 * See if the set of values represented by this new voucher
1563 * already exist in another voucher. If so return a reference
1564 * to the existing voucher and deallocate the voucher provided.
1565 * Otherwise, insert this one in the hash and return it.
1566 * Conditions:
1567 * A voucher reference is donated on entry.
1568 * Returns:
1569 * A voucher reference (may be different than on entry).
1570 */
1571static ipc_voucher_t
1572iv_dedup(ipc_voucher_t new_iv)
1573{
1574 boolean_t empty;
1575 iv_index_t sum;
1576 iv_index_t hash;
1577 ipc_voucher_t iv;
1578
1579 sum = iv_checksum(new_iv, &empty);
1580
1581 /* If all values are default, that's the empty (NULL) voucher */
1582 if (empty) {
1583 iv_dealloc(new_iv, FALSE);
1584 return IV_NULL;
1585 }
1586
1587 hash = IV_HASH_BUCKET(sum);
1588
1589 ivht_lock();
1590 queue_iterate(&ivht_bucket[hash], iv, ipc_voucher_t, iv_hash_link) {
1591 assert(iv->iv_hash == hash);
1592
1593 /* if not already deallocating and sums match... */
1594 if (0 < iv->iv_refs && iv->iv_sum == sum) {
1595 iv_refs_t refs;
1596 iv_index_t i;
1597
1598 assert(iv->iv_table_size <= new_iv->iv_table_size);
1599
1600 /* and common entries match... */
1601 for (i = 0; i < iv->iv_table_size; i++)
1602 if (iv->iv_table[i] != new_iv->iv_table[i])
1603 break;
1604 if (i < iv->iv_table_size)
1605 continue;
1606
1607 /* and all extra entries in new one are unused... */
1608 while (i < new_iv->iv_table_size)
1609 if (new_iv->iv_table[i++] != IV_UNUSED_VALINDEX)
1610 break;
1611 if (i < new_iv->iv_table_size)
1612 continue;
1613
1614 /* ... we found a match... */
1615
1616 /* can we get a ref before it hits 0
1617 *
1618 * This is thread safe. The reference is just an atomic
1619 * add. If the reference count is zero when we adjust it,
1620 * no other thread can have a reference to the voucher.
1621 * The dealloc code requires holding the ivht_lock, so
1622 * the voucher cannot be yanked out from under us.
1623 */
1624 refs = iv_reference(iv);
1625 if (1 == refs) {
1626 /* drats! going away. Put back to zero */
1627 iv->iv_refs = 0;
1628 continue;
1629 }
1630
1631 ivht_unlock();
1632
1633 /* referenced previous, so deallocate the new one */
1634 iv_dealloc(new_iv, FALSE);
1635 return iv;
1636 }
1637 }
1638
1639 /* add the new voucher to the hash, and return it */
1640 new_iv->iv_sum = sum;
1641 new_iv->iv_hash = hash;
1642 queue_enter(&ivht_bucket[hash], new_iv, ipc_voucher_t, iv_hash_link);
1643 ivht_count++;
1644 ivht_unlock();
1645
1646 /*
1647 * This code is disabled for KDEBUG_LEVEL_IST and KDEBUG_LEVEL_NONE
1648 */
1649#if (KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD)
1650 if (kdebug_enable & ~KDEBUG_ENABLE_PPT) {
1651 uintptr_t voucher_addr = VM_KERNEL_ADDRPERM((uintptr_t)new_iv);
1652 uintptr_t attr_tracepoints_needed = 0;
1653
1654 if (ipc_voucher_trace_contents) {
1655 /*
1656 * voucher_contents sizing is a bit more constrained
1657 * than might be obvious.
1658 *
1659 * This is typically a uint8_t typed array. However,
1660 * we want to access it as a uintptr_t to efficiently
1661 * copyout the data in tracepoints.
1662 *
1663 * This constrains the size to uintptr_t bytes, and
1664 * adds a minimimum alignment requirement equivalent
1665 * to a uintptr_t.
1666 *
1667 * Further constraining the size is the fact that it
1668 * is copied out 4 uintptr_t chunks at a time. We do
1669 * NOT want to run off the end of the array and copyout
1670 * random stack data.
1671 *
1672 * So the minimum size is 4 * sizeof(uintptr_t), and
1673 * the minimum alignment is uintptr_t aligned.
1674 */
1675
1676#define PAYLOAD_PER_TRACEPOINT (4 * sizeof(uintptr_t))
1677#define PAYLOAD_SIZE 1024
1678
1679 _Static_assert(PAYLOAD_SIZE % PAYLOAD_PER_TRACEPOINT == 0, "size invariant violated");
1680
1681 mach_voucher_attr_raw_recipe_array_size_t payload_size = PAYLOAD_SIZE;
1682 uintptr_t payload[PAYLOAD_SIZE / sizeof(uintptr_t)];
1683 kern_return_t kr;
1684
1685 kr = mach_voucher_extract_all_attr_recipes(new_iv, (mach_voucher_attr_raw_recipe_array_t)payload, &payload_size);
1686 if (KERN_SUCCESS == kr) {
1687 attr_tracepoints_needed = (payload_size + PAYLOAD_PER_TRACEPOINT - 1) / PAYLOAD_PER_TRACEPOINT;
1688
1689 /*
1690 * To prevent leaking data from the stack, we
1691 * need to zero data to the end of a tracepoint
1692 * payload.
1693 */
1694 size_t remainder = payload_size % PAYLOAD_PER_TRACEPOINT;
1695 if (remainder) {
1696 bzero((uint8_t*)payload + payload_size,
1697 PAYLOAD_PER_TRACEPOINT - remainder);
1698 }
1699 }
1700
1701 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_CREATE) | DBG_FUNC_NONE,
1702 voucher_addr,
1703 new_iv->iv_table_size, ivht_count, payload_size, 0);
1704
1705 uintptr_t index = 0;
1706 while (attr_tracepoints_needed--) {
1707 KERNEL_DEBUG_CONSTANT1(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_CREATE_ATTR_DATA) | DBG_FUNC_NONE,
1708 payload[index],
1709 payload[index+1],
1710 payload[index+2],
1711 payload[index+3],
1712 voucher_addr);
1713 index += 4;
1714 }
1715 } else {
1716 KERNEL_DEBUG_CONSTANT(MACHDBG_CODE(DBG_MACH_IPC,MACH_IPC_VOUCHER_CREATE) | DBG_FUNC_NONE,
1717 voucher_addr,
1718 new_iv->iv_table_size, ivht_count, 0, 0);
1719 }
1720 }
1721#endif /* KDEBUG_LEVEL >= KDEBUG_LEVEL_STANDARD */
1722
1723 return new_iv;
1724}
1725
1726/*
1727 * Routine: ipc_create_mach_voucher
1728 * Purpose:
1729 * Create a new mach voucher and initialize it with the
1730 * value(s) created by having the appropriate resource
1731 * managers interpret the supplied recipe commands and
1732 * data.
1733 * Conditions:
1734 * Nothing locked (may invoke user-space repeatedly).
1735 * Caller holds references on previous vouchers.
1736 * Previous vouchers are passed as voucher indexes.
1737 */
1738kern_return_t
1739ipc_create_mach_voucher(
1740 ipc_voucher_attr_raw_recipe_array_t recipes,
1741 ipc_voucher_attr_raw_recipe_array_size_t recipe_size,
1742 ipc_voucher_t *new_voucher)
1743{
1744 ipc_voucher_attr_recipe_t sub_recipe;
1745 ipc_voucher_attr_recipe_size_t recipe_used = 0;
1746 ipc_voucher_t voucher;
1747 kern_return_t kr = KERN_SUCCESS;
1748
1749 /* if nothing to do ... */
1750 if (0 == recipe_size) {
1751 *new_voucher = IV_NULL;
1752 return KERN_SUCCESS;
1753 }
1754
1755 /* allocate a voucher */
1756 voucher = iv_alloc(ivgt_keys_in_use);
1757 if (IV_NULL == voucher)
1758 return KERN_RESOURCE_SHORTAGE;
1759
1760 /* iterate over the recipe items */
1761 while (0 < recipe_size - recipe_used) {
1762
1763 if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
1764 kr = KERN_INVALID_ARGUMENT;
1765 break;
1766 }
1767
1768 /* find the next recipe */
1769 sub_recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
1770 if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
1771 kr = KERN_INVALID_ARGUMENT;
1772 break;
1773 }
1774 recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
1775
1776 kr = ipc_execute_voucher_recipe_command(voucher,
1777 sub_recipe->key,
1778 sub_recipe->command,
1779 sub_recipe->previous_voucher,
1780 sub_recipe->content,
1781 sub_recipe->content_size,
1782 FALSE);
1783 if (KERN_SUCCESS != kr)
1784 break;
1785 }
1786
1787 if (KERN_SUCCESS == kr) {
1788 *new_voucher = iv_dedup(voucher);
1789 } else {
1790 iv_dealloc(voucher, FALSE);
1791 *new_voucher = IV_NULL;
1792 }
1793 return kr;
1794}
1795
1796/*
1797 * Routine: ipc_voucher_attr_control_create_mach_voucher
1798 * Purpose:
1799 * Create a new mach voucher and initialize it with the
1800 * value(s) created by having the appropriate resource
1801 * managers interpret the supplied recipe commands and
1802 * data.
1803 *
1804 * The resource manager control's privilege over its
1805 * particular key value is reflected on to the execution
1806 * code, allowing internal commands (like setting a
1807 * key value handle directly, rather than having to
1808 * create a recipe, that will generate a callback just
1809 * to get the value.
1810 *
1811 * Conditions:
1812 * Nothing locked (may invoke user-space repeatedly).
1813 * Caller holds references on previous vouchers.
1814 * Previous vouchers are passed as voucher indexes.
1815 */
1816kern_return_t
1817ipc_voucher_attr_control_create_mach_voucher(
1818 ipc_voucher_attr_control_t control,
1819 ipc_voucher_attr_raw_recipe_array_t recipes,
1820 ipc_voucher_attr_raw_recipe_array_size_t recipe_size,
1821 ipc_voucher_t *new_voucher)
1822{
1823 mach_voucher_attr_key_t control_key;
1824 ipc_voucher_attr_recipe_t sub_recipe;
1825 ipc_voucher_attr_recipe_size_t recipe_used = 0;
1826 ipc_voucher_t voucher = IV_NULL;
1827 kern_return_t kr = KERN_SUCCESS;
1828
1829 if (IPC_VOUCHER_ATTR_CONTROL_NULL == control)
1830 return KERN_INVALID_CAPABILITY;
1831
1832 /* if nothing to do ... */
1833 if (0 == recipe_size) {
1834 *new_voucher = IV_NULL;
1835 return KERN_SUCCESS;
1836 }
1837
1838 /* allocate new voucher */
1839 voucher = iv_alloc(ivgt_keys_in_use);
1840 if (IV_NULL == voucher)
1841 return KERN_RESOURCE_SHORTAGE;
1842
1843 control_key = iv_index_to_key(control->ivac_key_index);
1844
1845 /* iterate over the recipe items */
1846 while (0 < recipe_size - recipe_used) {
1847
1848 if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
1849 kr = KERN_INVALID_ARGUMENT;
1850 break;
1851 }
1852
1853 /* find the next recipe */
1854 sub_recipe = (ipc_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
1855 if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
1856 kr = KERN_INVALID_ARGUMENT;
1857 break;
1858 }
1859 recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
1860
1861 kr = ipc_execute_voucher_recipe_command(voucher,
1862 sub_recipe->key,
1863 sub_recipe->command,
1864 sub_recipe->previous_voucher,
1865 sub_recipe->content,
1866 sub_recipe->content_size,
1867 (sub_recipe->key == control_key));
1868 if (KERN_SUCCESS != kr)
1869 break;
1870 }
1871
1872 if (KERN_SUCCESS == kr) {
1873 *new_voucher = iv_dedup(voucher);
1874 } else {
1875 *new_voucher = IV_NULL;
1876 iv_dealloc(voucher, FALSE);
1877 }
1878 return kr;
1879}
1880
1881/*
1882 * ipc_register_well_known_mach_voucher_attr_manager
1883 *
1884 * Register the resource manager responsible for a given key value.
1885 */
1886kern_return_t
1887ipc_register_well_known_mach_voucher_attr_manager(
1888 ipc_voucher_attr_manager_t manager,
1889 mach_voucher_attr_value_handle_t default_value,
1890 mach_voucher_attr_key_t key,
1891 ipc_voucher_attr_control_t *control)
1892{
1893 ipc_voucher_attr_control_t new_control;
1894 iv_index_t key_index;
1895 iv_index_t hash_index;
1896
1897 if (IVAM_NULL == manager)
1898 return KERN_INVALID_ARGUMENT;
1899
1900 key_index = iv_key_to_index(key);
1901 if (IV_UNUSED_KEYINDEX == key_index)
1902 return KERN_INVALID_ARGUMENT;
1903
1904 new_control = ivac_alloc(key_index);
1905 if (IVAC_NULL == new_control)
1906 return KERN_RESOURCE_SHORTAGE;
1907
1908 /* insert the default value into slot 0 */
1909 new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_value = default_value;
1910 new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_refs = IVACE_REFS_MAX;
1911 new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_made = IVACE_REFS_MAX;
1912 assert(IV_HASH_END == new_control->ivac_table[IV_UNUSED_VALINDEX].ivace_next);
1913
1914 ivgt_lock();
1915 if (IVAM_NULL != iv_global_table[key_index].ivgte_manager) {
1916 ivgt_unlock();
1917 ivac_release(new_control);
1918 return KERN_INVALID_ARGUMENT;
1919 }
1920
1921 /* fill in the global table slot for this key */
1922 iv_global_table[key_index].ivgte_manager = manager;
1923 iv_global_table[key_index].ivgte_control = new_control;
1924 iv_global_table[key_index].ivgte_key = key;
1925
1926 /* insert the default value into the hash (in case it is returned later) */
1927 hash_index = iv_hash_value(key_index, default_value);
1928 assert(IV_HASH_END == new_control->ivac_table[hash_index].ivace_index);
1929 new_control->ivac_table[hash_index].ivace_index = IV_UNUSED_VALINDEX;
1930
1931 ivgt_unlock();
1932
1933 /* return the reference on the new cache control to the caller */
1934 *control = new_control;
1935
1936 return KERN_SUCCESS;
1937}
1938
1939/*
1940 * Routine: mach_voucher_extract_attr_content
1941 * Purpose:
1942 * Extract the content for a given <voucher, key> pair.
1943 *
1944 * If a value other than the default is present for this
1945 * <voucher,key> pair, we need to contact the resource
1946 * manager to extract the content/meaning of the value(s)
1947 * present. Otherwise, return success (but no data).
1948 *
1949 * Conditions:
1950 * Nothing locked - as it may upcall to user-space.
1951 * The caller holds a reference on the voucher.
1952 */
1953kern_return_t
1954mach_voucher_extract_attr_content(
1955 ipc_voucher_t voucher,
1956 mach_voucher_attr_key_t key,
1957 mach_voucher_attr_content_t content,
1958 mach_voucher_attr_content_size_t *in_out_size)
1959{
1960 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
1961 mach_voucher_attr_value_handle_array_size_t vals_count;
1962 mach_voucher_attr_recipe_command_t command;
1963 ipc_voucher_attr_manager_t manager;
1964 iv_index_t value_index;
1965 iv_index_t key_index;
1966 kern_return_t kr;
1967
1968
1969 if (IV_NULL == voucher)
1970 return KERN_INVALID_ARGUMENT;
1971
1972 key_index = iv_key_to_index(key);
1973
1974 value_index = iv_lookup(voucher, key_index);
1975 if (IV_UNUSED_VALINDEX == value_index) {
1976 *in_out_size = 0;
1977 return KERN_SUCCESS;
1978 }
1979
1980 /*
1981 * Get the manager for this key_index. The
1982 * existence of a non-default value for this
1983 * slot within our voucher will keep the
1984 * manager referenced during the callout.
1985 */
1986 ivgt_lookup(key_index, FALSE, &manager, NULL);
1987 assert(IVAM_NULL != manager);
1988
1989 /*
1990 * Get the value(s) to pass to the manager
1991 * for this value_index.
1992 */
1993 ivace_lookup_values(key_index, value_index,
1994 vals, &vals_count);
1995 assert(0 < vals_count);
1996
1997 /* callout to manager */
1998
1999 kr = (manager->ivam_extract_content)(manager, key,
2000 vals, vals_count,
2001 &command,
2002 content, in_out_size);
2003 return kr;
2004}
2005
2006/*
2007 * Routine: mach_voucher_extract_attr_recipe
2008 * Purpose:
2009 * Extract a recipe for a given <voucher, key> pair.
2010 *
2011 * If a value other than the default is present for this
2012 * <voucher,key> pair, we need to contact the resource
2013 * manager to extract the content/meaning of the value(s)
2014 * present. Otherwise, return success (but no data).
2015 *
2016 * Conditions:
2017 * Nothing locked - as it may upcall to user-space.
2018 * The caller holds a reference on the voucher.
2019 */
2020kern_return_t
2021mach_voucher_extract_attr_recipe(
2022 ipc_voucher_t voucher,
2023 mach_voucher_attr_key_t key,
2024 mach_voucher_attr_raw_recipe_t raw_recipe,
2025 mach_voucher_attr_raw_recipe_size_t *in_out_size)
2026{
2027 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2028 mach_voucher_attr_value_handle_array_size_t vals_count;
2029 ipc_voucher_attr_manager_t manager;
2030 mach_voucher_attr_recipe_t recipe;
2031 iv_index_t value_index;
2032 iv_index_t key_index;
2033 kern_return_t kr;
2034
2035
2036 if (IV_NULL == voucher)
2037 return KERN_INVALID_ARGUMENT;
2038
2039 key_index = iv_key_to_index(key);
2040
2041 value_index = iv_lookup(voucher, key_index);
2042 if (IV_UNUSED_VALINDEX == value_index) {
2043 *in_out_size = 0;
2044 return KERN_SUCCESS;
2045 }
2046
2047 if (*in_out_size < sizeof(*recipe))
2048 return KERN_NO_SPACE;
2049
2050 recipe = (mach_voucher_attr_recipe_t)(void *)raw_recipe;
2051 recipe->key = key;
2052 recipe->command = MACH_VOUCHER_ATTR_NOOP;
2053 recipe->previous_voucher = MACH_VOUCHER_NAME_NULL;
2054 recipe->content_size = *in_out_size - sizeof(*recipe);
2055
2056 /*
2057 * Get the manager for this key_index. The
2058 * existence of a non-default value for this
2059 * slot within our voucher will keep the
2060 * manager referenced during the callout.
2061 */
2062 ivgt_lookup(key_index, FALSE, &manager, NULL);
2063 assert(IVAM_NULL != manager);
2064
2065 /*
2066 * Get the value(s) to pass to the manager
2067 * for this value_index.
2068 */
2069 ivace_lookup_values(key_index, value_index,
2070 vals, &vals_count);
2071 assert(0 < vals_count);
2072
2073 /* callout to manager */
2074 kr = (manager->ivam_extract_content)(manager, key,
2075 vals, vals_count,
2076 &recipe->command,
2077 recipe->content, &recipe->content_size);
2078 if (KERN_SUCCESS == kr) {
2079 assert(*in_out_size - sizeof(*recipe) >= recipe->content_size);
2080 *in_out_size = sizeof(*recipe) + recipe->content_size;
2081 }
2082
2083 return kr;
2084}
2085
2086
2087
2088/*
2089 * Routine: mach_voucher_extract_all_attr_recipes
2090 * Purpose:
2091 * Extract all the (non-default) contents for a given voucher,
2092 * building up a recipe that could be provided to a future
2093 * voucher creation call.
2094 * Conditions:
2095 * Nothing locked (may invoke user-space).
2096 * Caller holds a reference on the supplied voucher.
2097 */
2098kern_return_t
2099mach_voucher_extract_all_attr_recipes(
2100 ipc_voucher_t voucher,
2101 mach_voucher_attr_raw_recipe_array_t recipes,
2102 mach_voucher_attr_raw_recipe_array_size_t *in_out_size)
2103{
2104 mach_voucher_attr_recipe_size_t recipe_size = *in_out_size;
2105 mach_voucher_attr_recipe_size_t recipe_used = 0;
2106 iv_index_t key_index;
2107
2108 if (IV_NULL == voucher)
2109 return KERN_INVALID_ARGUMENT;
2110
2111 for (key_index = 0; key_index < voucher->iv_table_size; key_index++) {
2112 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2113 mach_voucher_attr_value_handle_array_size_t vals_count;
2114 mach_voucher_attr_content_size_t content_size;
2115 ipc_voucher_attr_manager_t manager;
2116 mach_voucher_attr_recipe_t recipe;
2117 mach_voucher_attr_key_t key;
2118 iv_index_t value_index;
2119 kern_return_t kr;
2120
2121 /* don't output anything for a default value */
2122 value_index = iv_lookup(voucher, key_index);
2123 if (IV_UNUSED_VALINDEX == value_index)
2124 continue;
2125
2126 if (recipe_size - recipe_used < sizeof(*recipe))
2127 return KERN_NO_SPACE;
2128
2129 recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2130 content_size = recipe_size - recipe_used - sizeof(*recipe);
2131
2132 /*
2133 * Get the manager for this key_index. The
2134 * existence of a non-default value for this
2135 * slot within our voucher will keep the
2136 * manager referenced during the callout.
2137 */
2138 ivgt_lookup(key_index, FALSE, &manager, NULL);
2139 assert(IVAM_NULL != manager);
2140
2141 /*
2142 * Get the value(s) to pass to the manager
2143 * for this value_index.
2144 */
2145 ivace_lookup_values(key_index, value_index,
2146 vals, &vals_count);
2147 assert(0 < vals_count);
2148
2149 key = iv_index_to_key(key_index);
2150
2151 recipe->key = key;
2152 recipe->command = MACH_VOUCHER_ATTR_NOOP;
2153 recipe->content_size = content_size;
2154
2155 /* callout to manager */
2156 kr = (manager->ivam_extract_content)(manager, key,
2157 vals, vals_count,
2158 &recipe->command,
2159 recipe->content, &recipe->content_size);
2160 if (KERN_SUCCESS != kr)
2161 return kr;
2162
2163 assert(recipe->content_size <= content_size);
2164 recipe_used += sizeof(*recipe) + recipe->content_size;
2165 }
2166
2167 *in_out_size = recipe_used;
2168 return KERN_SUCCESS;
2169}
2170
2171/*
2172 * Routine: mach_voucher_debug_info
2173 * Purpose:
2174 * Extract all the (non-default) contents for a given mach port name,
2175 * building up a recipe that could be provided to a future
2176 * voucher creation call.
2177 * Conditions:
2178 * Nothing locked (may invoke user-space).
2179 * Caller may not hold a reference on the supplied voucher.
2180 */
2181#if !(DEVELOPMENT || DEBUG)
2182kern_return_t
2183mach_voucher_debug_info(
2184 ipc_space_t __unused space,
2185 mach_port_name_t __unused voucher_name,
2186 mach_voucher_attr_raw_recipe_array_t __unused recipes,
2187 mach_voucher_attr_raw_recipe_array_size_t __unused *in_out_size)
2188{
2189 return KERN_NOT_SUPPORTED;
2190}
2191#else
2192kern_return_t
2193mach_voucher_debug_info(
2194 ipc_space_t space,
2195 mach_port_name_t voucher_name,
2196 mach_voucher_attr_raw_recipe_array_t recipes,
2197 mach_voucher_attr_raw_recipe_array_size_t *in_out_size)
2198{
2199 ipc_voucher_t voucher = IPC_VOUCHER_NULL;
2200 kern_return_t kr;
2201 ipc_port_t port = MACH_PORT_NULL;
2202
2203 if (!MACH_PORT_VALID(voucher_name)) {
2204 return KERN_INVALID_ARGUMENT;
2205 }
2206
2207 kr = ipc_port_translate_send(space, voucher_name, &port);
2208 if (KERN_SUCCESS != kr)
2209 return KERN_INVALID_ARGUMENT;
2210
2211 voucher = convert_port_to_voucher(port);
2212 ip_unlock(port);
2213
2214 if (voucher) {
2215 kr = mach_voucher_extract_all_attr_recipes(voucher, recipes, in_out_size);
2216 ipc_voucher_release(voucher);
2217 return kr;
2218 }
2219
2220 return KERN_FAILURE;
2221}
2222#endif
2223
2224/*
2225 * Routine: mach_voucher_attr_command
2226 * Purpose:
2227 * Invoke an attribute-specific command through this voucher.
2228 *
2229 * The voucher layout, membership, etc... is not altered
2230 * through the execution of this command.
2231 *
2232 * Conditions:
2233 * Nothing locked - as it may upcall to user-space.
2234 * The caller holds a reference on the voucher.
2235 */
2236kern_return_t
2237mach_voucher_attr_command(
2238 ipc_voucher_t voucher,
2239 mach_voucher_attr_key_t key,
2240 mach_voucher_attr_command_t command,
2241 mach_voucher_attr_content_t in_content,
2242 mach_voucher_attr_content_size_t in_content_size,
2243 mach_voucher_attr_content_t out_content,
2244 mach_voucher_attr_content_size_t *out_content_size)
2245{
2246 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2247 mach_voucher_attr_value_handle_array_size_t vals_count;
2248 ipc_voucher_attr_manager_t manager;
2249 ipc_voucher_attr_control_t control;
2250 iv_index_t value_index;
2251 iv_index_t key_index;
2252 kern_return_t kr;
2253
2254
2255 if (IV_NULL == voucher)
2256 return KERN_INVALID_ARGUMENT;
2257
2258 key_index = iv_key_to_index(key);
2259
2260 /*
2261 * Get the manager for this key_index.
2262 * Allowing commands against the default value
2263 * for an attribute means that we have to hold
2264 * reference on the attribute manager control
2265 * to keep the manager around during the command
2266 * execution.
2267 */
2268 ivgt_lookup(key_index, TRUE, &manager, &control);
2269 assert(IVAM_NULL != manager);
2270
2271 /*
2272 * Get the values for this <voucher, key> pair
2273 * to pass to the attribute manager. It is still
2274 * permissible to execute a command against the
2275 * default value (empty value array).
2276 */
2277 value_index = iv_lookup(voucher, key_index);
2278 ivace_lookup_values(key_index, value_index,
2279 vals, &vals_count);
2280
2281 /* callout to manager */
2282 kr = (manager->ivam_command)(manager, key,
2283 vals, vals_count,
2284 command,
2285 in_content, in_content_size,
2286 out_content, out_content_size);
2287
2288 /* release reference on control */
2289 ivac_release(control);
2290
2291 return kr;
2292}
2293
2294/*
2295 * Routine: mach_voucher_attr_control_get_values
2296 * Purpose:
2297 * For a given voucher, get the value handle associated with the
2298 * specified attribute manager.
2299 */
2300kern_return_t
2301mach_voucher_attr_control_get_values(
2302 ipc_voucher_attr_control_t control,
2303 ipc_voucher_t voucher,
2304 mach_voucher_attr_value_handle_array_t out_values,
2305 mach_voucher_attr_value_handle_array_size_t *in_out_size)
2306{
2307 iv_index_t key_index, value_index;
2308
2309 if (IPC_VOUCHER_ATTR_CONTROL_NULL == control)
2310 return KERN_INVALID_CAPABILITY;
2311
2312 if (IV_NULL == voucher)
2313 return KERN_INVALID_ARGUMENT;
2314
2315 if (0 == *in_out_size)
2316 return KERN_SUCCESS;
2317
2318 key_index = control->ivac_key_index;
2319
2320 assert(0 < voucher->iv_refs);
2321 value_index = iv_lookup(voucher, key_index);
2322 ivace_lookup_values(key_index, value_index,
2323 out_values, in_out_size);
2324 return KERN_SUCCESS;
2325}
2326
2327
2328/*
2329 * Routine: mach_voucher_attr_control_create_mach_voucher
2330 * Purpose:
2331 * Create a new mach voucher and initialize it by processing the
2332 * supplied recipe(s).
2333 *
2334 * Coming in on the attribute control port denotes special privileges
2335 * over they key associated with the control port.
2336 *
2337 * Coming in from user-space, each recipe item will have a previous
2338 * recipe port name that needs to be converted to a voucher. Because
2339 * we can't rely on the port namespace to hold a reference on each
2340 * previous voucher port for the duration of processing that command,
2341 * we have to convert the name to a voucher reference and release it
2342 * after the command processing is done.
2343 */
2344kern_return_t
2345mach_voucher_attr_control_create_mach_voucher(
2346 ipc_voucher_attr_control_t control,
2347 mach_voucher_attr_raw_recipe_array_t recipes,
2348 mach_voucher_attr_raw_recipe_size_t recipe_size,
2349 ipc_voucher_t *new_voucher)
2350{
2351 mach_voucher_attr_key_t control_key;
2352 mach_voucher_attr_recipe_t sub_recipe;
2353 mach_voucher_attr_recipe_size_t recipe_used = 0;
2354 ipc_voucher_t voucher = IV_NULL;
2355 kern_return_t kr = KERN_SUCCESS;
2356
2357 if (IPC_VOUCHER_ATTR_CONTROL_NULL == control)
2358 return KERN_INVALID_CAPABILITY;
2359
2360 /* if nothing to do ... */
2361 if (0 == recipe_size) {
2362 *new_voucher = IV_NULL;
2363 return KERN_SUCCESS;
2364 }
2365
2366 /* allocate new voucher */
2367 voucher = iv_alloc(ivgt_keys_in_use);
2368 if (IV_NULL == voucher)
2369 return KERN_RESOURCE_SHORTAGE;
2370
2371 control_key = iv_index_to_key(control->ivac_key_index);
2372
2373 /* iterate over the recipe items */
2374 while (0 < recipe_size - recipe_used) {
2375 ipc_voucher_t prev_iv;
2376
2377 if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
2378 kr = KERN_INVALID_ARGUMENT;
2379 break;
2380 }
2381
2382 /* find the next recipe */
2383 sub_recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2384 if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
2385 kr = KERN_INVALID_ARGUMENT;
2386 break;
2387 }
2388 recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
2389
2390 /* convert voucher port name (current space) into a voucher reference */
2391 prev_iv = convert_port_name_to_voucher(sub_recipe->previous_voucher);
2392 if (MACH_PORT_NULL != sub_recipe->previous_voucher && IV_NULL == prev_iv) {
2393 kr = KERN_INVALID_CAPABILITY;
2394 break;
2395 }
2396
2397 kr = ipc_execute_voucher_recipe_command(voucher,
2398 sub_recipe->key,
2399 sub_recipe->command,
2400 prev_iv,
2401 sub_recipe->content,
2402 sub_recipe->content_size,
2403 (sub_recipe->key == control_key));
2404 ipc_voucher_release(prev_iv);
2405
2406 if (KERN_SUCCESS != kr)
2407 break;
2408 }
2409
2410 if (KERN_SUCCESS == kr) {
2411 *new_voucher = iv_dedup(voucher);
2412 } else {
2413 *new_voucher = IV_NULL;
2414 iv_dealloc(voucher, FALSE);
2415 }
2416 return kr;
2417}
2418
2419/*
2420 * Routine: host_create_mach_voucher
2421 * Purpose:
2422 * Create a new mach voucher and initialize it by processing the
2423 * supplied recipe(s).
2424 *
2425 * Comming in from user-space, each recipe item will have a previous
2426 * recipe port name that needs to be converted to a voucher. Because
2427 * we can't rely on the port namespace to hold a reference on each
2428 * previous voucher port for the duration of processing that command,
2429 * we have to convert the name to a voucher reference and release it
2430 * after the command processing is done.
2431 */
2432kern_return_t
2433host_create_mach_voucher(
2434 host_t host,
2435 mach_voucher_attr_raw_recipe_array_t recipes,
2436 mach_voucher_attr_raw_recipe_size_t recipe_size,
2437 ipc_voucher_t *new_voucher)
2438{
2439 mach_voucher_attr_recipe_t sub_recipe;
2440 mach_voucher_attr_recipe_size_t recipe_used = 0;
2441 ipc_voucher_t voucher = IV_NULL;
2442 kern_return_t kr = KERN_SUCCESS;
2443
2444 if (host == HOST_NULL)
2445 return KERN_INVALID_ARGUMENT;
2446
2447 /* if nothing to do ... */
2448 if (0 == recipe_size) {
2449 *new_voucher = IV_NULL;
2450 return KERN_SUCCESS;
2451 }
2452
2453 /* allocate new voucher */
2454 voucher = iv_alloc(ivgt_keys_in_use);
2455 if (IV_NULL == voucher)
2456 return KERN_RESOURCE_SHORTAGE;
2457
2458 /* iterate over the recipe items */
2459 while (0 < recipe_size - recipe_used) {
2460 ipc_voucher_t prev_iv;
2461
2462 if (recipe_size - recipe_used < sizeof(*sub_recipe)) {
2463 kr = KERN_INVALID_ARGUMENT;
2464 break;
2465 }
2466
2467 /* find the next recipe */
2468 sub_recipe = (mach_voucher_attr_recipe_t)(void *)&recipes[recipe_used];
2469 if (recipe_size - recipe_used - sizeof(*sub_recipe) < sub_recipe->content_size) {
2470 kr = KERN_INVALID_ARGUMENT;
2471 break;
2472 }
2473 recipe_used += sizeof(*sub_recipe) + sub_recipe->content_size;
2474
2475 /* convert voucher port name (current space) into a voucher reference */
2476 prev_iv = convert_port_name_to_voucher(sub_recipe->previous_voucher);
2477 if (MACH_PORT_NULL != sub_recipe->previous_voucher && IV_NULL == prev_iv) {
2478 kr = KERN_INVALID_CAPABILITY;
2479 break;
2480 }
2481
2482 kr = ipc_execute_voucher_recipe_command(voucher,
2483 sub_recipe->key,
2484 sub_recipe->command,
2485 prev_iv,
2486 sub_recipe->content,
2487 sub_recipe->content_size,
2488 FALSE);
2489 ipc_voucher_release(prev_iv);
2490
2491 if (KERN_SUCCESS != kr)
2492 break;
2493 }
2494
2495 if (KERN_SUCCESS == kr) {
2496 *new_voucher = iv_dedup(voucher);
2497 } else {
2498 *new_voucher = IV_NULL;
2499 iv_dealloc(voucher, FALSE);
2500 }
2501 return kr;
2502}
2503
2504/*
2505 * Routine: host_register_well_known_mach_voucher_attr_manager
2506 * Purpose:
2507 * Register the user-level resource manager responsible for a given
2508 * key value.
2509 * Conditions:
2510 * The manager port passed in has to be converted/wrapped
2511 * in an ipc_voucher_attr_manager_t structure and then call the
2512 * internal variant. We have a generic ipc voucher manager
2513 * type that implements a MIG proxy out to user-space just for
2514 * this purpose.
2515 */
2516kern_return_t
2517host_register_well_known_mach_voucher_attr_manager(
2518 host_t host,
2519 mach_voucher_attr_manager_t __unused manager,
2520 mach_voucher_attr_value_handle_t __unused default_value,
2521 mach_voucher_attr_key_t __unused key,
2522 ipc_voucher_attr_control_t __unused *control)
2523{
2524 if (HOST_NULL == host)
2525 return KERN_INVALID_HOST;
2526
2527#if 1
2528 return KERN_NOT_SUPPORTED;
2529#else
2530 /*
2531 * Allocate a mig_voucher_attr_manager_t that provides the
2532 * MIG proxy functions for the three manager callbacks and
2533 * store the port right in there.
2534 *
2535 * If the user-space manager dies, we'll detect it on our
2536 * next upcall, and cleanup the proxy at that point.
2537 */
2538 mig_voucher_attr_manager_t proxy;
2539 kern_return_t kr;
2540
2541 proxy = mvam_alloc(manager);
2542
2543 kr = ipc_register_well_known_mach_voucher_attr_manager(&proxy->mvam_manager,
2544 default_value,
2545 key,
2546 control);
2547 if (KERN_SUCCESS != kr)
2548 mvam_release(proxy);
2549
2550 return kr;
2551#endif
2552}
2553
2554/*
2555 * Routine: host_register_mach_voucher_attr_manager
2556 * Purpose:
2557 * Register the user-space resource manager and return a
2558 * dynamically allocated key.
2559 * Conditions:
2560 * Wrap the supplied port with the MIG proxy ipc
2561 * voucher resource manager, and then call the internal
2562 * variant.
2563 */
2564kern_return_t
2565host_register_mach_voucher_attr_manager(
2566 host_t host,
2567 mach_voucher_attr_manager_t __unused manager,
2568 mach_voucher_attr_value_handle_t __unused default_value,
2569 mach_voucher_attr_key_t __unused *key,
2570 ipc_voucher_attr_control_t __unused *control)
2571{
2572 if (HOST_NULL == host)
2573 return KERN_INVALID_HOST;
2574
2575 return KERN_NOT_SUPPORTED;
2576}
2577
2578
2579#if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2580
2581/*
2582 * Build-in a simple User Data Resource Manager
2583 */
2584#define USER_DATA_MAX_DATA (16*1024)
2585
2586struct user_data_value_element {
2587 mach_voucher_attr_value_reference_t e_made;
2588 mach_voucher_attr_content_size_t e_size;
2589 iv_index_t e_sum;
2590 iv_index_t e_hash;
2591 queue_chain_t e_hash_link;
2592 uint8_t e_data[];
2593};
2594
2595typedef struct user_data_value_element *user_data_element_t;
2596
2597/*
2598 * User Data Voucher Hash Table
2599 */
2600#define USER_DATA_HASH_BUCKETS 127
2601#define USER_DATA_HASH_BUCKET(x) ((x) % USER_DATA_HASH_BUCKETS)
2602
2603static queue_head_t user_data_bucket[USER_DATA_HASH_BUCKETS];
2604static lck_spin_t user_data_lock_data;
2605
2606#define user_data_lock_init() \
2607 lck_spin_init(&user_data_lock_data, &ipc_lck_grp, &ipc_lck_attr)
2608#define user_data_lock_destroy() \
2609 lck_spin_destroy(&user_data_lock_data, &ipc_lck_grp)
2610#define user_data_lock() \
2611 lck_spin_lock(&user_data_lock_data)
2612#define user_data_lock_try() \
2613 lck_spin_try_lock(&user_data_lock_data)
2614#define user_data_unlock() \
2615 lck_spin_unlock(&user_data_lock_data)
2616
2617static kern_return_t
2618user_data_release_value(
2619 ipc_voucher_attr_manager_t manager,
2620 mach_voucher_attr_key_t key,
2621 mach_voucher_attr_value_handle_t value,
2622 mach_voucher_attr_value_reference_t sync);
2623
2624static kern_return_t
2625user_data_get_value(
2626 ipc_voucher_attr_manager_t manager,
2627 mach_voucher_attr_key_t key,
2628 mach_voucher_attr_recipe_command_t command,
2629 mach_voucher_attr_value_handle_array_t prev_values,
2630 mach_voucher_attr_value_handle_array_size_t prev_value_count,
2631 mach_voucher_attr_content_t content,
2632 mach_voucher_attr_content_size_t content_size,
2633 mach_voucher_attr_value_handle_t *out_value,
2634 ipc_voucher_t *out_value_voucher);
2635
2636static kern_return_t
2637user_data_extract_content(
2638 ipc_voucher_attr_manager_t manager,
2639 mach_voucher_attr_key_t key,
2640 mach_voucher_attr_value_handle_array_t values,
2641 mach_voucher_attr_value_handle_array_size_t value_count,
2642 mach_voucher_attr_recipe_command_t *out_command,
2643 mach_voucher_attr_content_t out_content,
2644 mach_voucher_attr_content_size_t *in_out_content_size);
2645
2646static kern_return_t
2647user_data_command(
2648 ipc_voucher_attr_manager_t manager,
2649 mach_voucher_attr_key_t key,
2650 mach_voucher_attr_value_handle_array_t values,
2651 mach_msg_type_number_t value_count,
2652 mach_voucher_attr_command_t command,
2653 mach_voucher_attr_content_t in_content,
2654 mach_voucher_attr_content_size_t in_content_size,
2655 mach_voucher_attr_content_t out_content,
2656 mach_voucher_attr_content_size_t *out_content_size);
2657
2658static void
2659user_data_release(
2660 ipc_voucher_attr_manager_t manager);
2661
2662struct ipc_voucher_attr_manager user_data_manager = {
2663 .ivam_release_value = user_data_release_value,
2664 .ivam_get_value = user_data_get_value,
2665 .ivam_extract_content = user_data_extract_content,
2666 .ivam_command = user_data_command,
2667 .ivam_release = user_data_release,
2668};
2669
2670ipc_voucher_attr_control_t user_data_control;
2671ipc_voucher_attr_control_t test_control;
2672
2673#if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) && defined(MACH_VOUCHER_ATTR_KEY_TEST)
2674#define USER_DATA_ASSERT_KEY(key) \
2675 assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key) || \
2676 MACH_VOUCHER_ATTR_KEY_TEST == (key));
2677#elif defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
2678#define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_USER_DATA == (key))
2679#else
2680#define USER_DATA_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_TEST == (key))
2681#endif
2682
2683/*
2684 * Routine: user_data_release_value
2685 * Purpose:
2686 * Release a made reference on a specific value managed by
2687 * this voucher attribute manager.
2688 * Conditions:
2689 * Must remove the element associated with this value from
2690 * the hash if this is the last know made reference.
2691 */
2692static kern_return_t
2693user_data_release_value(
2694 ipc_voucher_attr_manager_t __assert_only manager,
2695 mach_voucher_attr_key_t __assert_only key,
2696 mach_voucher_attr_value_handle_t value,
2697 mach_voucher_attr_value_reference_t sync)
2698{
2699 user_data_element_t elem;
2700 iv_index_t hash;
2701
2702 assert (&user_data_manager == manager);
2703 USER_DATA_ASSERT_KEY(key);
2704
2705 elem = (user_data_element_t)value;
2706 hash = elem->e_hash;
2707
2708 user_data_lock();
2709 if (sync == elem->e_made) {
2710 queue_remove(&user_data_bucket[hash], elem, user_data_element_t, e_hash_link);
2711 user_data_unlock();
2712 kfree(elem, sizeof(*elem) + elem->e_size);
2713 return KERN_SUCCESS;
2714 }
2715 assert(sync < elem->e_made);
2716 user_data_unlock();
2717
2718 return KERN_FAILURE;
2719}
2720
2721/*
2722 * Routine: user_data_checksum
2723 * Purpose:
2724 * Provide a rudimentary checksum for the data presented
2725 * to these voucher attribute managers.
2726 */
2727static iv_index_t
2728user_data_checksum(
2729 mach_voucher_attr_content_t content,
2730 mach_voucher_attr_content_size_t content_size)
2731{
2732 mach_voucher_attr_content_size_t i;
2733 iv_index_t cksum = 0;
2734
2735 for(i = 0; i < content_size; i++, content++) {
2736 cksum = (cksum << 8) ^ (cksum + *(unsigned char *)content);
2737 }
2738
2739 return (~cksum);
2740}
2741
2742/*
2743 * Routine: user_data_dedup
2744 * Purpose:
2745 * See if the content represented by this request already exists
2746 * in another user data element. If so return a made reference
2747 * to the existing element. Otherwise, create a new element and
2748 * return that (after inserting it in the hash).
2749 * Conditions:
2750 * Nothing locked.
2751 * Returns:
2752 * A made reference on the user_data_element_t
2753 */
2754static user_data_element_t
2755user_data_dedup(
2756 mach_voucher_attr_content_t content,
2757 mach_voucher_attr_content_size_t content_size)
2758{
2759 iv_index_t sum;
2760 iv_index_t hash;
2761 user_data_element_t elem;
2762 user_data_element_t alloc = NULL;
2763
2764 sum = user_data_checksum(content, content_size);
2765 hash = USER_DATA_HASH_BUCKET(sum);
2766
2767 retry:
2768 user_data_lock();
2769 queue_iterate(&user_data_bucket[hash], elem, user_data_element_t, e_hash_link) {
2770 assert(elem->e_hash == hash);
2771
2772 /* if sums match... */
2773 if (elem->e_sum == sum && elem->e_size == content_size) {
2774 iv_index_t i;
2775
2776 /* and all data matches */
2777 for (i = 0; i < content_size; i++)
2778 if (elem->e_data[i] != content[i])
2779 break;
2780 if (i < content_size)
2781 continue;
2782
2783 /* ... we found a match... */
2784
2785 elem->e_made++;
2786 user_data_unlock();
2787
2788 if (NULL != alloc)
2789 kfree(alloc, sizeof(*alloc) + content_size);
2790
2791 return elem;
2792 }
2793 }
2794
2795 if (NULL == alloc) {
2796 user_data_unlock();
2797
2798 alloc = (user_data_element_t)kalloc(sizeof(*alloc) + content_size);
2799 alloc->e_made = 1;
2800 alloc->e_size = content_size;
2801 alloc->e_sum = sum;
2802 alloc->e_hash = hash;
2803 memcpy(alloc->e_data, content, content_size);
2804 goto retry;
2805 }
2806
2807 queue_enter(&user_data_bucket[hash], alloc, user_data_element_t, e_hash_link);
2808 user_data_unlock();
2809
2810 return alloc;
2811}
2812
2813static kern_return_t
2814user_data_get_value(
2815 ipc_voucher_attr_manager_t __assert_only manager,
2816 mach_voucher_attr_key_t __assert_only key,
2817 mach_voucher_attr_recipe_command_t command,
2818 mach_voucher_attr_value_handle_array_t prev_values,
2819 mach_voucher_attr_value_handle_array_size_t prev_value_count,
2820 mach_voucher_attr_content_t content,
2821 mach_voucher_attr_content_size_t content_size,
2822 mach_voucher_attr_value_handle_t *out_value,
2823 ipc_voucher_t *out_value_voucher)
2824{
2825 user_data_element_t elem;
2826
2827 assert (&user_data_manager == manager);
2828 USER_DATA_ASSERT_KEY(key);
2829
2830 /* never an out voucher */
2831 *out_value_voucher = IPC_VOUCHER_NULL;
2832
2833 switch (command) {
2834
2835 case MACH_VOUCHER_ATTR_REDEEM:
2836
2837 /* redeem of previous values is the value */
2838 if (0 < prev_value_count) {
2839 elem = (user_data_element_t)prev_values[0];
2840 assert(0 < elem->e_made);
2841 elem->e_made++;
2842 *out_value = prev_values[0];
2843 return KERN_SUCCESS;
2844 }
2845
2846 /* redeem of default is default */
2847 *out_value = 0;
2848 return KERN_SUCCESS;
2849
2850 case MACH_VOUCHER_ATTR_USER_DATA_STORE:
2851 if (USER_DATA_MAX_DATA < content_size)
2852 return KERN_RESOURCE_SHORTAGE;
2853
2854 /* empty is the default */
2855 if (0 == content_size) {
2856 *out_value = 0;
2857 return KERN_SUCCESS;
2858 }
2859
2860 elem = user_data_dedup(content, content_size);
2861 *out_value = (mach_voucher_attr_value_handle_t)elem;
2862 return KERN_SUCCESS;
2863
2864 default:
2865 /* every other command is unknown */
2866 return KERN_INVALID_ARGUMENT;
2867 }
2868}
2869
2870static kern_return_t
2871user_data_extract_content(
2872 ipc_voucher_attr_manager_t __assert_only manager,
2873 mach_voucher_attr_key_t __assert_only key,
2874 mach_voucher_attr_value_handle_array_t values,
2875 mach_voucher_attr_value_handle_array_size_t value_count,
2876 mach_voucher_attr_recipe_command_t *out_command,
2877 mach_voucher_attr_content_t out_content,
2878 mach_voucher_attr_content_size_t *in_out_content_size)
2879{
2880 mach_voucher_attr_content_size_t size = 0;
2881 user_data_element_t elem;
2882 unsigned int i;
2883
2884 assert (&user_data_manager == manager);
2885 USER_DATA_ASSERT_KEY(key);
2886
2887 /* concatenate the stored data items */
2888 for (i = 0; i < value_count ; i++) {
2889 elem = (user_data_element_t)values[i];
2890 assert(USER_DATA_MAX_DATA >= elem->e_size);
2891
2892 if (size + elem->e_size > *in_out_content_size)
2893 return KERN_NO_SPACE;
2894
2895 memcpy(&out_content[size], elem->e_data, elem->e_size);
2896 size += elem->e_size;
2897 }
2898 *out_command = MACH_VOUCHER_ATTR_BITS_STORE;
2899 *in_out_content_size = size;
2900 return KERN_SUCCESS;
2901}
2902
2903static kern_return_t
2904user_data_command(
2905 ipc_voucher_attr_manager_t __assert_only manager,
2906 mach_voucher_attr_key_t __assert_only key,
2907 mach_voucher_attr_value_handle_array_t __unused values,
2908 mach_msg_type_number_t __unused value_count,
2909 mach_voucher_attr_command_t __unused command,
2910 mach_voucher_attr_content_t __unused in_content,
2911 mach_voucher_attr_content_size_t __unused in_content_size,
2912 mach_voucher_attr_content_t __unused out_content,
2913 mach_voucher_attr_content_size_t __unused *out_content_size)
2914{
2915 assert (&user_data_manager == manager);
2916 USER_DATA_ASSERT_KEY(key);
2917 return KERN_FAILURE;
2918}
2919
2920static void
2921user_data_release(
2922 ipc_voucher_attr_manager_t manager)
2923{
2924 if (manager != &user_data_manager)
2925 return;
2926
2927 panic("Voucher user-data manager released");
2928}
2929
2930static int user_data_manager_inited = 0;
2931
2932void
2933user_data_attr_manager_init()
2934{
2935 kern_return_t kr;
2936
2937#if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA)
2938 if ((user_data_manager_inited & 0x1) != 0x1) {
2939 kr = ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager,
2940 (mach_voucher_attr_value_handle_t)0,
2941 MACH_VOUCHER_ATTR_KEY_USER_DATA,
2942 &user_data_control);
2943 if (KERN_SUCCESS != kr)
2944 printf("Voucher user-data manager register(USER-DATA) returned %d", kr);
2945 else
2946 user_data_manager_inited |= 0x1;
2947 }
2948#endif
2949#if defined(MACH_VOUCHER_ATTR_KEY_TEST)
2950 if ((user_data_manager_inited & 0x2) != 0x2) {
2951 kr = ipc_register_well_known_mach_voucher_attr_manager(&user_data_manager,
2952 (mach_voucher_attr_value_handle_t)0,
2953 MACH_VOUCHER_ATTR_KEY_TEST,
2954 &test_control);
2955 if (KERN_SUCCESS != kr)
2956 printf("Voucher user-data manager register(TEST) returned %d", kr);
2957 else
2958 user_data_manager_inited |= 0x2;
2959 }
2960#endif
2961#if defined(MACH_VOUCHER_ATTR_KEY_USER_DATA) || defined(MACH_VOUCHER_ATTR_KEY_TEST)
2962 int i;
2963
2964 for (i=0; i < USER_DATA_HASH_BUCKETS; i++)
2965 queue_init(&user_data_bucket[i]);
2966
2967 user_data_lock_init();
2968#endif
2969}
2970
2971#endif /* MACH_DEBUG */