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