1/* 2 * Copyright (c) 1999-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 * Mach Operating System 30 * Copyright (c) 1991,1990,1989 Carnegie Mellon University 31 * All Rights Reserved. 32 * 33 * Permission to use, copy, modify and distribute this software and its 34 * documentation is hereby granted, provided that both the copyright 35 * notice and this permission notice appear in all copies of the 36 * software, derivative works or modified versions, and any portions 37 * thereof, and that both notices appear in supporting documentation. 38 * 39 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 40 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 41 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 42 * 43 * Carnegie Mellon requests users of this software to return to 44 * 45 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 46 * School of Computer Science 47 * Carnegie Mellon University 48 * Pittsburgh PA 15213-3890 49 * 50 * any improvements or extensions that they make and grant Carnegie Mellon 51 * the rights to redistribute these changes. 52 */ 53 54#include <stdlib.h> 55#include <mach/mach.h> 56#include <mach/boolean.h> 57#include <mach/kern_return.h> 58#include <mach/message.h> 59#include <mach/mig_errors.h> 60#include <mach/vm_statistics.h> 61#include <TargetConditionals.h> 62 63extern int proc_importance_assertion_begin_with_msg(mach_msg_header_t * msg, mach_msg_trailer_t * trailer, uint64_t * assertion_handlep); 64extern int proc_importance_assertion_complete(uint64_t assertion_handle); 65 66#define MACH_MSG_TRAP(msg, opt, ssize, rsize, rname, to, not) \ 67 mach_msg_trap((msg), (opt), (ssize), (rsize), (rname), (to), (not)) 68 69#define LIBMACH_OPTIONS (MACH_SEND_INTERRUPT|MACH_RCV_INTERRUPT) 70 71/* 72 * Routine: mach_msg 73 * Purpose: 74 * Send and/or receive a message. If the message operation 75 * is interrupted, and the user did not request an indication 76 * of that fact, then restart the appropriate parts of the 77 * operation. 78 */ 79mach_msg_return_t 80mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify) 81 mach_msg_header_t *msg; 82 mach_msg_option_t option; 83 mach_msg_size_t send_size; 84 mach_msg_size_t rcv_size; 85 mach_port_t rcv_name; 86 mach_msg_timeout_t timeout; 87 mach_port_t notify; 88{ 89 mach_msg_return_t mr; 90 91 /* 92 * Consider the following cases: 93 * 1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED 94 * plus special bits). 95 * 2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options. 96 * 3) RPC calls with interruptions in one/both halves. 97 * 98 * We refrain from passing the option bits that we implement 99 * to the kernel. This prevents their presence from inhibiting 100 * the kernel's fast paths (when it checks the option value). 101 */ 102 103 mr = MACH_MSG_TRAP(msg, option &~ LIBMACH_OPTIONS, 104 send_size, rcv_size, rcv_name, 105 timeout, notify); 106 if (mr == MACH_MSG_SUCCESS) 107 return MACH_MSG_SUCCESS; 108 109 if ((option & MACH_SEND_INTERRUPT) == 0) 110 while (mr == MACH_SEND_INTERRUPTED) 111 mr = MACH_MSG_TRAP(msg, 112 option &~ LIBMACH_OPTIONS, 113 send_size, rcv_size, rcv_name, 114 timeout, notify); 115 116 if ((option & MACH_RCV_INTERRUPT) == 0) 117 while (mr == MACH_RCV_INTERRUPTED) 118 mr = MACH_MSG_TRAP(msg, 119 option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG), 120 0, rcv_size, rcv_name, 121 timeout, notify); 122 123 return mr; 124} 125 126/* 127 * Routine: mach_msg_overwrite 128 * Purpose: 129 * Send and/or receive a message. If the message operation 130 * is interrupted, and the user did not request an indication 131 * of that fact, then restart the appropriate parts of the 132 * operation. 133 * 134 * Distinct send and receive buffers may be specified. If 135 * no separate receive buffer is specified, the msg parameter 136 * will be used for both send and receive operations. 137 * 138 * In addition to a distinct receive buffer, that buffer may 139 * already contain scatter control information to direct the 140 * receiving of the message. 141 */ 142mach_msg_return_t 143mach_msg_overwrite(msg, option, send_size, rcv_limit, rcv_name, timeout, 144 notify, rcv_msg, rcv_scatter_size) 145 mach_msg_header_t *msg; 146 mach_msg_option_t option; 147 mach_msg_size_t send_size; 148 mach_msg_size_t rcv_limit; 149 mach_port_t rcv_name; 150 mach_msg_timeout_t timeout; 151 mach_port_t notify; 152 mach_msg_header_t *rcv_msg; 153 mach_msg_size_t rcv_scatter_size; 154{ 155 mach_msg_return_t mr; 156 157 /* 158 * Consider the following cases: 159 * 1) Errors in pseudo-receive (eg, MACH_SEND_INTERRUPTED 160 * plus special bits). 161 * 2) Use of MACH_SEND_INTERRUPT/MACH_RCV_INTERRUPT options. 162 * 3) RPC calls with interruptions in one/both halves. 163 * 164 * We refrain from passing the option bits that we implement 165 * to the kernel. This prevents their presence from inhibiting 166 * the kernel's fast paths (when it checks the option value). 167 */ 168 169 mr = mach_msg_overwrite_trap(msg, option &~ LIBMACH_OPTIONS, 170 send_size, rcv_limit, rcv_name, 171 timeout, notify, rcv_msg, rcv_scatter_size); 172 if (mr == MACH_MSG_SUCCESS) 173 return MACH_MSG_SUCCESS; 174 175 if ((option & MACH_SEND_INTERRUPT) == 0) 176 while (mr == MACH_SEND_INTERRUPTED) 177 mr = mach_msg_overwrite_trap(msg, 178 option &~ LIBMACH_OPTIONS, 179 send_size, rcv_limit, rcv_name, 180 timeout, notify, rcv_msg, rcv_scatter_size); 181 182 if ((option & MACH_RCV_INTERRUPT) == 0) 183 while (mr == MACH_RCV_INTERRUPTED) 184 mr = mach_msg_overwrite_trap(msg, 185 option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG), 186 0, rcv_limit, rcv_name, 187 timeout, notify, rcv_msg, rcv_scatter_size); 188 189 return mr; 190} 191 192 193mach_msg_return_t 194mach_msg_send(mach_msg_header_t *msg) 195{ 196 return mach_msg(msg, MACH_SEND_MSG, 197 msg->msgh_size, 0, MACH_PORT_NULL, 198 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 199} 200 201mach_msg_return_t 202mach_msg_receive(mach_msg_header_t *msg) 203{ 204 return mach_msg(msg, MACH_RCV_MSG, 205 0, msg->msgh_size, msg->msgh_local_port, 206 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 207} 208 209 210static void 211mach_msg_destroy_port(mach_port_t port, mach_msg_type_name_t type) 212{ 213 if (MACH_PORT_VALID(port)) switch (type) { 214 case MACH_MSG_TYPE_MOVE_SEND: 215 case MACH_MSG_TYPE_MOVE_SEND_ONCE: 216 /* destroy the send/send-once right */ 217 (void) mach_port_deallocate(mach_task_self_, port); 218 break; 219 220 case MACH_MSG_TYPE_MOVE_RECEIVE: 221 /* destroy the receive right */ 222 (void) mach_port_mod_refs(mach_task_self_, port, 223 MACH_PORT_RIGHT_RECEIVE, -1); 224 break; 225 226 case MACH_MSG_TYPE_MAKE_SEND: 227 /* create a send right and then destroy it */ 228 (void) mach_port_insert_right(mach_task_self_, port, 229 port, MACH_MSG_TYPE_MAKE_SEND); 230 (void) mach_port_deallocate(mach_task_self_, port); 231 break; 232 233 case MACH_MSG_TYPE_MAKE_SEND_ONCE: 234 /* create a send-once right and then destroy it */ 235 (void) mach_port_extract_right(mach_task_self_, port, 236 MACH_MSG_TYPE_MAKE_SEND_ONCE, 237 &port, &type); 238 (void) mach_port_deallocate(mach_task_self_, port); 239 break; 240 } 241} 242 243static void 244mach_msg_destroy_memory(vm_offset_t addr, vm_size_t size) 245{ 246 if (size != 0) 247 (void) vm_deallocate(mach_task_self_, addr, size); 248} 249 250 251/* 252 * Routine: mach_msg_destroy 253 * Purpose: 254 * mach_msg_destroy is useful in two contexts. 255 * 256 * First, it can deallocate all port rights and 257 * out-of-line memory in a received message. 258 * When a server receives a request it doesn't want, 259 * it needs this functionality. 260 * 261 * Second, it can mimic the side-effects of a msg-send 262 * operation. The effect is as if the message were sent 263 * and then destroyed inside the kernel. When a server 264 * can't send a reply (because the client died), 265 * it needs this functionality. 266 */ 267void 268mach_msg_destroy(mach_msg_header_t *msg) 269{ 270 mach_msg_bits_t mbits = msg->msgh_bits; 271 272 /* 273 * The msgh_local_port field doesn't hold a port right. 274 * The receive operation consumes the destination port right. 275 */ 276 277 mach_msg_destroy_port(msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(mbits)); 278 mach_msg_destroy_port(msg->msgh_voucher_port, MACH_MSGH_BITS_VOUCHER(mbits)); 279 280 if (mbits & MACH_MSGH_BITS_COMPLEX) { 281 mach_msg_base_t *base; 282 mach_msg_type_number_t count, i; 283 mach_msg_descriptor_t *daddr; 284 285 base = (mach_msg_base_t *) msg; 286 count = base->body.msgh_descriptor_count; 287 288 daddr = (mach_msg_descriptor_t *) (base + 1); 289 for (i = 0; i < count; i++) { 290 291 switch (daddr->type.type) { 292 293 case MACH_MSG_PORT_DESCRIPTOR: { 294 mach_msg_port_descriptor_t *dsc; 295 296 /* 297 * Destroy port rights carried in the message 298 */ 299 dsc = &daddr->port; 300 mach_msg_destroy_port(dsc->name, dsc->disposition); 301 daddr = (mach_msg_descriptor_t *)(dsc + 1); 302 break; 303 } 304 305 case MACH_MSG_OOL_DESCRIPTOR: { 306 mach_msg_ool_descriptor_t *dsc; 307 308 /* 309 * Destroy memory carried in the message 310 */ 311 dsc = &daddr->out_of_line; 312 if (dsc->deallocate) { 313 mach_msg_destroy_memory((vm_offset_t)dsc->address, 314 dsc->size); 315 } 316 daddr = (mach_msg_descriptor_t *)(dsc + 1); 317 break; 318 } 319 320 case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: { 321 mach_msg_ool_descriptor_t *dsc; 322 323 /* 324 * Just skip it. 325 */ 326 dsc = &daddr->out_of_line; 327 daddr = (mach_msg_descriptor_t *)(dsc + 1); 328 break; 329 } 330 331 case MACH_MSG_OOL_PORTS_DESCRIPTOR: { 332 mach_port_t *ports; 333 mach_msg_ool_ports_descriptor_t *dsc; 334 mach_msg_type_number_t j; 335 336 /* 337 * Destroy port rights carried in the message 338 */ 339 dsc = &daddr->ool_ports; 340 ports = (mach_port_t *) dsc->address; 341 for (j = 0; j < dsc->count; j++, ports++) { 342 mach_msg_destroy_port(*ports, dsc->disposition); 343 } 344 345 /* 346 * Destroy memory carried in the message 347 */ 348 if (dsc->deallocate) { 349 mach_msg_destroy_memory((vm_offset_t)dsc->address, 350 dsc->count * sizeof(mach_port_t)); 351 } 352 daddr = (mach_msg_descriptor_t *)(dsc + 1); 353 break; 354 } 355 } 356 } 357 } 358} 359 360/* 361 * Routine: mach_msg_server_once 362 * Purpose: 363 * A simple generic server function. It allows more flexibility 364 * than mach_msg_server by processing only one message request 365 * and then returning to the user. Note that more in the way 366 * of error codes are returned to the user; specifically, any 367 * failing error from mach_msg calls will be returned 368 * (though errors from the demux routine or the routine it 369 * calls will not be). 370 */ 371mach_msg_return_t 372mach_msg_server_once( 373 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), 374 mach_msg_size_t max_size, 375 mach_port_t rcv_name, 376 mach_msg_options_t options) 377{ 378 mig_reply_error_t *bufRequest, *bufReply; 379 mach_msg_size_t request_size; 380 mach_msg_size_t request_alloc; 381 mach_msg_size_t trailer_alloc; 382 mach_msg_size_t reply_alloc; 383 mach_msg_return_t mr; 384 kern_return_t kr; 385 mach_port_t self = mach_task_self_; 386 voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED; 387 388 options &= ~(MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_VOUCHER); 389 390 trailer_alloc = REQUESTED_TRAILER_SIZE(options); 391 request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc); 392 393 request_size = (options & MACH_RCV_LARGE) ? 394 request_alloc : max_size + trailer_alloc; 395 396 reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ? 397 (max_size + MAX_TRAILER_SIZE) : 398 max_size); 399 400 kr = vm_allocate(self, 401 (vm_address_t *)&bufReply, 402 reply_alloc, 403 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 404 if (kr != KERN_SUCCESS) 405 return kr; 406 407 for (;;) { 408 mach_msg_size_t new_request_alloc; 409 410 kr = vm_allocate(self, 411 (vm_address_t *)&bufRequest, 412 request_alloc, 413 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 414 if (kr != KERN_SUCCESS) { 415 vm_deallocate(self, 416 (vm_address_t)bufReply, 417 reply_alloc); 418 return kr; 419 } 420 421 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|MACH_RCV_VOUCHER|options, 422 0, request_size, rcv_name, 423 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 424 425 if (!((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE))) 426 break; 427 428 new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size + 429 trailer_alloc); 430 vm_deallocate(self, 431 (vm_address_t) bufRequest, 432 request_alloc); 433 request_size = request_alloc = new_request_alloc; 434 } 435 436 if (mr == MACH_MSG_SUCCESS) { 437 /* we have a request message */ 438 439 old_state = voucher_mach_msg_adopt(&bufRequest->Head); 440 441 (void) (*demux)(&bufRequest->Head, &bufReply->Head); 442 443 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 444 if (bufReply->RetCode == MIG_NO_REPLY) 445 bufReply->Head.msgh_remote_port = MACH_PORT_NULL; 446 else if ((bufReply->RetCode != KERN_SUCCESS) && 447 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 448 /* destroy the request - but not the reply port */ 449 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; 450 mach_msg_destroy(&bufRequest->Head); 451 } 452 } 453 454 /* 455 * We don't want to block indefinitely because the client 456 * isn't receiving messages from the reply port. 457 * If we have a send-once right for the reply port, then 458 * this isn't a concern because the send won't block. 459 * If we have a send right, we need to use MACH_SEND_TIMEOUT. 460 * To avoid falling off the kernel's fast RPC path unnecessarily, 461 * we only supply MACH_SEND_TIMEOUT when absolutely necessary. 462 */ 463 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) { 464 465 mr = mach_msg(&bufReply->Head, 466 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == 467 MACH_MSG_TYPE_MOVE_SEND_ONCE) ? 468 MACH_SEND_MSG|options : 469 MACH_SEND_MSG|MACH_SEND_TIMEOUT|options, 470 bufReply->Head.msgh_size, 0, MACH_PORT_NULL, 471 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 472 473 if ((mr != MACH_SEND_INVALID_DEST) && 474 (mr != MACH_SEND_TIMED_OUT)) 475 goto done_once; 476 mr = MACH_MSG_SUCCESS; 477 } 478 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) 479 mach_msg_destroy(&bufReply->Head); 480 } 481 482 done_once: 483 voucher_mach_msg_revert(old_state); 484 485 (void)vm_deallocate(self, 486 (vm_address_t) bufRequest, 487 request_alloc); 488 (void)vm_deallocate(self, 489 (vm_address_t) bufReply, 490 reply_alloc); 491 return mr; 492} 493 494/* 495 * Routine: mach_msg_server 496 * Purpose: 497 * A simple generic server function. Note that changes here 498 * should be considered for duplication above. 499 */ 500mach_msg_return_t 501mach_msg_server( 502 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), 503 mach_msg_size_t max_size, 504 mach_port_t rcv_name, 505 mach_msg_options_t options) 506{ 507 mig_reply_error_t *bufRequest, *bufReply; 508 mach_msg_size_t request_size; 509 mach_msg_size_t new_request_alloc; 510 mach_msg_size_t request_alloc; 511 mach_msg_size_t trailer_alloc; 512 mach_msg_size_t reply_alloc; 513 mach_msg_return_t mr; 514 kern_return_t kr; 515 mach_port_t self = mach_task_self_; 516 voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED; 517 boolean_t buffers_swapped = FALSE; 518 519 options &= ~(MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_VOUCHER|MACH_RCV_OVERWRITE); 520 521 reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ? 522 (max_size + MAX_TRAILER_SIZE) : max_size); 523 524 kr = vm_allocate(self, 525 (vm_address_t *)&bufReply, 526 reply_alloc, 527 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 528 if (kr != KERN_SUCCESS) 529 return kr; 530 531 request_alloc = 0; 532 trailer_alloc = REQUESTED_TRAILER_SIZE(options); 533 new_request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc); 534 535 request_size = (options & MACH_RCV_LARGE) ? 536 new_request_alloc : max_size + trailer_alloc; 537 538 for (;;) { 539 if (request_alloc < new_request_alloc) { 540 request_alloc = new_request_alloc; 541 kr = vm_allocate(self, 542 (vm_address_t *)&bufRequest, 543 request_alloc, 544 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 545 if (kr != KERN_SUCCESS) { 546 vm_deallocate(self, 547 (vm_address_t)bufReply, 548 reply_alloc); 549 return kr; 550 } 551 } 552 553 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|MACH_RCV_VOUCHER|options, 554 0, request_size, rcv_name, 555 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 556 557 while (mr == MACH_MSG_SUCCESS) { 558 /* we have another request message */ 559 560 buffers_swapped = FALSE; 561 old_state = voucher_mach_msg_adopt(&bufRequest->Head); 562 563 (void) (*demux)(&bufRequest->Head, &bufReply->Head); 564 565 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 566 if (bufReply->RetCode == MIG_NO_REPLY) 567 bufReply->Head.msgh_remote_port = MACH_PORT_NULL; 568 else if ((bufReply->RetCode != KERN_SUCCESS) && 569 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 570 /* destroy the request - but not the reply port */ 571 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; 572 mach_msg_destroy(&bufRequest->Head); 573 } 574 } 575 576 /* 577 * We don't want to block indefinitely because the client 578 * isn't receiving messages from the reply port. 579 * If we have a send-once right for the reply port, then 580 * this isn't a concern because the send won't block. 581 * If we have a send right, we need to use MACH_SEND_TIMEOUT. 582 * To avoid falling off the kernel's fast RPC path, 583 * we only supply MACH_SEND_TIMEOUT when absolutely necessary. 584 */ 585 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) { 586 if (request_alloc == reply_alloc) { 587 mig_reply_error_t *bufTemp; 588 589 mr = mach_msg( 590 &bufReply->Head, 591 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == 592 MACH_MSG_TYPE_MOVE_SEND_ONCE) ? 593 MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options : 594 MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options, 595 bufReply->Head.msgh_size, request_size, rcv_name, 596 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 597 598 /* swap request and reply */ 599 bufTemp = bufRequest; 600 bufRequest = bufReply; 601 bufReply = bufTemp; 602 buffers_swapped = TRUE; 603 } else { 604 mr = mach_msg_overwrite( 605 &bufReply->Head, 606 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == 607 MACH_MSG_TYPE_MOVE_SEND_ONCE) ? 608 MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options : 609 MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options, 610 bufReply->Head.msgh_size, request_size, rcv_name, 611 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, 612 &bufRequest->Head, 0); 613 } 614 615 if ((mr != MACH_SEND_INVALID_DEST) && 616 (mr != MACH_SEND_TIMED_OUT) && 617 (mr != MACH_RCV_TIMED_OUT)) { 618 619 voucher_mach_msg_revert(old_state); 620 old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED; 621 622 continue; 623 } 624 } 625 /* 626 * Need to destroy the reply msg in case if there was a send timeout or 627 * invalid destination. The reply msg would be swapped with request msg 628 * if buffers_swapped is true, thus destroy request msg instead of 629 * reply msg in such cases. 630 */ 631 if (mr != MACH_RCV_TIMED_OUT) { 632 if (buffers_swapped) { 633 if (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) 634 mach_msg_destroy(&bufRequest->Head); 635 } else { 636 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) 637 mach_msg_destroy(&bufReply->Head); 638 } 639 } 640 voucher_mach_msg_revert(old_state); 641 old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED; 642 643 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|MACH_RCV_VOUCHER|options, 644 0, request_size, rcv_name, 645 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 646 647 } /* while (mr == MACH_MSG_SUCCESS) */ 648 649 if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) { 650 new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size + 651 trailer_alloc); 652 request_size = new_request_alloc; 653 vm_deallocate(self, 654 (vm_address_t) bufRequest, 655 request_alloc); 656 continue; 657 } 658 659 break; 660 661 } /* for(;;) */ 662 663 (void)vm_deallocate(self, 664 (vm_address_t) bufRequest, 665 request_alloc); 666 (void)vm_deallocate(self, 667 (vm_address_t) bufReply, 668 reply_alloc); 669 return mr; 670} 671 672/* 673 * Routine: mach_msg_server_importance 674 * Purpose: 675 * A simple generic server function which handles importance 676 * promotion assertions for adaptive daemons. 677 */ 678mach_msg_return_t 679mach_msg_server_importance( 680 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), 681 mach_msg_size_t max_size, 682 mach_port_t rcv_name, 683 mach_msg_options_t options) 684{ 685 mig_reply_error_t *bufRequest, *bufReply; 686 mach_msg_size_t request_size; 687 mach_msg_size_t new_request_alloc; 688 mach_msg_size_t request_alloc; 689 mach_msg_size_t trailer_alloc; 690 mach_msg_size_t reply_alloc; 691 mach_msg_return_t mr; 692 kern_return_t kr; 693 mach_port_t self = mach_task_self_; 694 int retval = 1; 695 uint64_t token; 696 voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED; 697 698 options &= ~(MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_VOUCHER|MACH_RCV_OVERWRITE); 699 700 reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ? 701 (max_size + MAX_TRAILER_SIZE) : max_size); 702 703 kr = vm_allocate(self, 704 (vm_address_t *)&bufReply, 705 reply_alloc, 706 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 707 if (kr != KERN_SUCCESS) 708 return kr; 709 710 request_alloc = 0; 711 trailer_alloc = REQUESTED_TRAILER_SIZE(options); 712 new_request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc); 713 714 request_size = (options & MACH_RCV_LARGE) ? 715 new_request_alloc : max_size + trailer_alloc; 716 717 for (;;) { 718 if (request_alloc < new_request_alloc) { 719 request_alloc = new_request_alloc; 720 kr = vm_allocate(self, 721 (vm_address_t *)&bufRequest, 722 request_alloc, 723 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 724 if (kr != KERN_SUCCESS) { 725 vm_deallocate(self, 726 (vm_address_t)bufReply, 727 reply_alloc); 728 return kr; 729 } 730 } 731 732 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|MACH_RCV_VOUCHER|options, 733 0, request_size, rcv_name, 734 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 735 736 if (mr == MACH_MSG_SUCCESS) { 737 /* we have another request message */ 738 739 old_state = voucher_mach_msg_adopt(&bufRequest->Head); 740 741 retval = proc_importance_assertion_begin_with_msg(&bufRequest->Head, NULL, &token); 742 743 (void) (*demux)(&bufRequest->Head, &bufReply->Head); 744 745 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 746 if (bufReply->RetCode == MIG_NO_REPLY) 747 bufReply->Head.msgh_remote_port = MACH_PORT_NULL; 748 else if ((bufReply->RetCode != KERN_SUCCESS) && 749 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 750 /* destroy the request - but not the reply port */ 751 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; 752 mach_msg_destroy(&bufRequest->Head); 753 } 754 } 755 756 /* 757 * We don't want to block indefinitely because the client 758 * isn't receiving messages from the reply port. 759 * If we have a send-once right for the reply port, then 760 * this isn't a concern because the send won't block. 761 * If we have a send right, we need to use MACH_SEND_TIMEOUT. 762 * To avoid falling off the kernel's fast RPC path, 763 * we only supply MACH_SEND_TIMEOUT when absolutely necessary. 764 */ 765 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) { 766 767 mr = mach_msg( 768 &bufReply->Head, 769 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == 770 MACH_MSG_TYPE_MOVE_SEND_ONCE) ? 771 MACH_SEND_MSG|options : 772 MACH_SEND_MSG|MACH_SEND_TIMEOUT|options, 773 bufReply->Head.msgh_size, 0, MACH_PORT_NULL, 774 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 775 776 if ((mr != MACH_SEND_INVALID_DEST) && 777 (mr != MACH_SEND_TIMED_OUT)) { 778 if (retval == 0) 779 proc_importance_assertion_complete(token); 780 781 voucher_mach_msg_revert(old_state); 782 old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED; 783 784 continue; 785 } 786 mr = MACH_MSG_SUCCESS; 787 } 788 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) 789 mach_msg_destroy(&bufReply->Head); 790 if (retval == 0) 791 proc_importance_assertion_complete(token); 792 793 voucher_mach_msg_revert(old_state); 794 old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED; 795 796 } /* if (mr == MACH_MSG_SUCCESS) */ 797 798 if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) { 799 new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size + 800 trailer_alloc); 801 request_size = new_request_alloc; 802 vm_deallocate(self, 803 (vm_address_t) bufRequest, 804 request_alloc); 805 continue; 806 } else if (mr == MACH_MSG_SUCCESS) 807 continue; 808 else 809 break; 810 811 } /* for(;;) */ 812 813 (void)vm_deallocate(self, 814 (vm_address_t) bufRequest, 815 request_alloc); 816 (void)vm_deallocate(self, 817 (vm_address_t) bufReply, 818 reply_alloc); 819 return mr; 820} 821 822kern_return_t 823mach_voucher_deallocate( 824 mach_voucher_t voucher) 825{ 826 return mach_port_deallocate(mach_task_self(), voucher); 827} 828