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