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