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