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