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