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