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