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