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