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