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