]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ipc/ipc_voucher.c
xnu-2782.1.97.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_voucher.c
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 */
49 uint32_t ipc_voucher_trace_contents = 0;
50
51 static zone_t ipc_voucher_zone;
52 static 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
60 static queue_head_t ivht_bucket[IV_HASH_BUCKETS];
61 static lck_spin_t ivht_lock_data;
62 static 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 */
82 static iv_index_t ivgt_keys_in_use = MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN;
83 static ipc_voucher_global_table_element iv_global_table[MACH_VOUCHER_ATTR_KEY_NUM_WELL_KNOWN];
84 static 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
97 ipc_voucher_t iv_alloc(iv_index_t entries);
98 void iv_dealloc(ipc_voucher_t iv, boolean_t unhash);
99
100 static inline iv_refs_t
101 iv_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
109 static inline void
110 iv_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
132 static inline iv_index_t
133 iv_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 */
155 static inline iv_index_t
156 iv_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
164 static inline mach_voucher_attr_key_t
165 iv_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
173 static void ivace_release(iv_index_t key_index, iv_index_t value_index);
174 static 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
178 static iv_index_t iv_lookup(ipc_voucher_t, iv_index_t);
179
180
181 static 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)
188 void user_data_attr_manager_init(void);
189 #endif
190
191 void
192 ipc_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
223 ipc_voucher_t
224 iv_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 */
270 static void
271 iv_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
279 void
280 iv_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 */
338 static inline iv_index_t
339 iv_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 */
359 uintptr_t
360 unsafe_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 */
387 ipc_voucher_t
388 convert_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
419 ipc_voucher_t
420 convert_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
440 void
441 ipc_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
452 void
453 ipc_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 */
472 void
473 ipc_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 */
489 ipc_port_t
490 convert_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
557 ipc_voucher_attr_control_t
558 ivac_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
593 void
594 ivac_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
653 void
654 ipc_voucher_attr_control_reference(ipc_voucher_attr_control_t control)
655 {
656 ivac_reference(control);
657 }
658
659 void
660 ipc_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 */
674 ipc_voucher_attr_control_t
675 convert_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
698 void
699 ipc_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 */
722 ipc_port_t
723 convert_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 */
770 static void
771 ivace_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 */
810 static void
811 ivac_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 */
886 static void
887 ivace_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 */
920 static iv_index_t
921 ivace_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);
934 restart:
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 */
997 static 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 */
1143 static void
1144 ivgt_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 */
1184 static kern_return_t
1185 ipc_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 */
1275 static kern_return_t
1276 ipc_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
1319 static kern_return_t
1320 ipc_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 */
1542 static inline iv_index_t
1543 iv_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 */
1577 static ipc_voucher_t
1578 iv_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 */
1744 kern_return_t
1745 ipc_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 */
1822 kern_return_t
1823 ipc_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 */
1892 kern_return_t
1893 ipc_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 */
1959 kern_return_t
1960 mach_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 */
2026 kern_return_t
2027 mach_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 */
2104 kern_return_t
2105 mach_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)
2188 kern_return_t
2189 mach_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
2198 kern_return_t
2199 mach_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 */
2242 kern_return_t
2243 mach_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 */
2306 kern_return_t
2307 mach_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 */
2350 kern_return_t
2351 mach_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 */
2438 kern_return_t
2439 host_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 */
2522 kern_return_t
2523 host_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 */
2570 kern_return_t
2571 host_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
2592 struct 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
2601 typedef 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
2609 static queue_head_t user_data_bucket[USER_DATA_HASH_BUCKETS];
2610 static 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
2623 static kern_return_t
2624 user_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
2630 static kern_return_t
2631 user_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
2642 static kern_return_t
2643 user_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
2652 static kern_return_t
2653 user_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
2664 static void
2665 user_data_release(
2666 ipc_voucher_attr_manager_t manager);
2667
2668 struct 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
2676 ipc_voucher_attr_control_t user_data_control;
2677 ipc_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 */
2698 static kern_return_t
2699 user_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 */
2733 static iv_index_t
2734 user_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 */
2760 static user_data_element_t
2761 user_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
2819 static kern_return_t
2820 user_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
2876 static kern_return_t
2877 user_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
2909 static kern_return_t
2910 user_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
2926 static void
2927 user_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
2936 static int user_data_manager_inited = 0;
2937
2938 void
2939 user_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 */