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