1/* 2 * Copyright (c) 1999-2007 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 279 if (mbits & MACH_MSGH_BITS_COMPLEX) { 280 mach_msg_base_t *base; 281 mach_msg_type_number_t count, i; 282 mach_msg_descriptor_t *daddr; 283 284 base = (mach_msg_base_t *) msg; 285 count = base->body.msgh_descriptor_count; 286 287 daddr = (mach_msg_descriptor_t *) (base + 1); 288 for (i = 0; i < count; i++) { 289 290 switch (daddr->type.type) { 291 292 case MACH_MSG_PORT_DESCRIPTOR: { 293 mach_msg_port_descriptor_t *dsc; 294 295 /* 296 * Destroy port rights carried in the message 297 */ 298 dsc = &daddr->port; 299 mach_msg_destroy_port(dsc->name, dsc->disposition); 300 daddr = (mach_msg_descriptor_t *)(dsc + 1); 301 break; 302 } 303 304 case MACH_MSG_OOL_DESCRIPTOR: { 305 mach_msg_ool_descriptor_t *dsc; 306 307 /* 308 * Destroy memory carried in the message 309 */ 310 dsc = &daddr->out_of_line; 311 if (dsc->deallocate) { 312 mach_msg_destroy_memory((vm_offset_t)dsc->address, 313 dsc->size); 314 } 315 daddr = (mach_msg_descriptor_t *)(dsc + 1); 316 break; 317 } 318 319 case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: { 320 mach_msg_ool_descriptor_t *dsc; 321 322 /* 323 * Just skip it. 324 */ 325 dsc = &daddr->out_of_line; 326 daddr = (mach_msg_descriptor_t *)(dsc + 1); 327 break; 328 } 329 330 case MACH_MSG_OOL_PORTS_DESCRIPTOR: { 331 mach_port_t *ports; 332 mach_msg_ool_ports_descriptor_t *dsc; 333 mach_msg_type_number_t j; 334 335 /* 336 * Destroy port rights carried in the message 337 */ 338 dsc = &daddr->ool_ports; 339 ports = (mach_port_t *) dsc->address; 340 for (j = 0; j < dsc->count; j++, ports++) { 341 mach_msg_destroy_port(*ports, dsc->disposition); 342 } 343 344 /* 345 * Destroy memory carried in the message 346 */ 347 if (dsc->deallocate) { 348 mach_msg_destroy_memory((vm_offset_t)dsc->address, 349 dsc->count * sizeof(mach_port_t)); 350 } 351 daddr = (mach_msg_descriptor_t *)(dsc + 1); 352 break; 353 } 354 } 355 } 356 } 357} 358 359/* 360 * Routine: mach_msg_server_once 361 * Purpose: 362 * A simple generic server function. It allows more flexibility 363 * than mach_msg_server by processing only one message request 364 * and then returning to the user. Note that more in the way 365 * of error codes are returned to the user; specifically, any 366 * failing error from mach_msg calls will be returned 367 * (though errors from the demux routine or the routine it 368 * calls will not be). 369 */ 370mach_msg_return_t 371mach_msg_server_once( 372 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), 373 mach_msg_size_t max_size, 374 mach_port_t rcv_name, 375 mach_msg_options_t options) 376{ 377 mig_reply_error_t *bufRequest, *bufReply; 378 mach_msg_size_t request_size; 379 mach_msg_size_t request_alloc; 380 mach_msg_size_t trailer_alloc; 381 mach_msg_size_t reply_alloc; 382 mach_msg_return_t mr; 383 kern_return_t kr; 384 mach_port_t self = mach_task_self_; 385 386 options &= ~(MACH_SEND_MSG|MACH_RCV_MSG); 387 388 trailer_alloc = REQUESTED_TRAILER_SIZE(options); 389 request_alloc = round_page(max_size + trailer_alloc); 390 391 request_size = (options & MACH_RCV_LARGE) ? 392 request_alloc : max_size + trailer_alloc; 393 394 reply_alloc = round_page((options & MACH_SEND_TRAILER) ? 395 (max_size + MAX_TRAILER_SIZE) : 396 max_size); 397 398 kr = vm_allocate(self, 399 (vm_address_t *)&bufReply, 400 reply_alloc, 401 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 402 if (kr != KERN_SUCCESS) 403 return kr; 404 405 for (;;) { 406 mach_msg_size_t new_request_alloc; 407 408 kr = vm_allocate(self, 409 (vm_address_t *)&bufRequest, 410 request_alloc, 411 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 412 if (kr != KERN_SUCCESS) { 413 vm_deallocate(self, 414 (vm_address_t)bufReply, 415 reply_alloc); 416 return kr; 417 } 418 419 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options, 420 0, request_size, rcv_name, 421 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 422 423 if (!((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE))) 424 break; 425 426 new_request_alloc = round_page(bufRequest->Head.msgh_size + 427 trailer_alloc); 428 vm_deallocate(self, 429 (vm_address_t) bufRequest, 430 request_alloc); 431 request_size = request_alloc = new_request_alloc; 432 } 433 434 if (mr == MACH_MSG_SUCCESS) { 435 /* we have a request message */ 436 437 (void) (*demux)(&bufRequest->Head, &bufReply->Head); 438 439 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 440 if (bufReply->RetCode == MIG_NO_REPLY) 441 bufReply->Head.msgh_remote_port = MACH_PORT_NULL; 442 else if ((bufReply->RetCode != KERN_SUCCESS) && 443 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 444 /* destroy the request - but not the reply port */ 445 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; 446 mach_msg_destroy(&bufRequest->Head); 447 } 448 } 449 450 /* 451 * We don't want to block indefinitely because the client 452 * isn't receiving messages from the reply port. 453 * If we have a send-once right for the reply port, then 454 * this isn't a concern because the send won't block. 455 * If we have a send right, we need to use MACH_SEND_TIMEOUT. 456 * To avoid falling off the kernel's fast RPC path unnecessarily, 457 * we only supply MACH_SEND_TIMEOUT when absolutely necessary. 458 */ 459 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) { 460 461 mr = mach_msg(&bufReply->Head, 462 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == 463 MACH_MSG_TYPE_MOVE_SEND_ONCE) ? 464 MACH_SEND_MSG|options : 465 MACH_SEND_MSG|MACH_SEND_TIMEOUT|options, 466 bufReply->Head.msgh_size, 0, MACH_PORT_NULL, 467 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 468 469 if ((mr != MACH_SEND_INVALID_DEST) && 470 (mr != MACH_SEND_TIMED_OUT)) 471 goto done_once; 472 mr = MACH_MSG_SUCCESS; 473 } 474 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) 475 mach_msg_destroy(&bufReply->Head); 476 } 477 478 done_once: 479 (void)vm_deallocate(self, 480 (vm_address_t) bufRequest, 481 request_alloc); 482 (void)vm_deallocate(self, 483 (vm_address_t) bufReply, 484 reply_alloc); 485 return mr; 486} 487 488/* 489 * Routine: mach_msg_server 490 * Purpose: 491 * A simple generic server function. Note that changes here 492 * should be considered for duplication above. 493 */ 494mach_msg_return_t 495mach_msg_server( 496 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), 497 mach_msg_size_t max_size, 498 mach_port_t rcv_name, 499 mach_msg_options_t options) 500{ 501 mig_reply_error_t *bufRequest, *bufReply; 502 mach_msg_size_t request_size; 503 mach_msg_size_t new_request_alloc; 504 mach_msg_size_t request_alloc; 505 mach_msg_size_t trailer_alloc; 506 mach_msg_size_t reply_alloc; 507 mach_msg_return_t mr; 508 kern_return_t kr; 509 mach_port_t self = mach_task_self_; 510 511 options &= ~(MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_OVERWRITE); 512 513 reply_alloc = round_page((options & MACH_SEND_TRAILER) ? 514 (max_size + MAX_TRAILER_SIZE) : max_size); 515 516 kr = vm_allocate(self, 517 (vm_address_t *)&bufReply, 518 reply_alloc, 519 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 520 if (kr != KERN_SUCCESS) 521 return kr; 522 523 request_alloc = 0; 524 trailer_alloc = REQUESTED_TRAILER_SIZE(options); 525 new_request_alloc = round_page(max_size + trailer_alloc); 526 527 request_size = (options & MACH_RCV_LARGE) ? 528 new_request_alloc : max_size + trailer_alloc; 529 530 for (;;) { 531 if (request_alloc < new_request_alloc) { 532 request_alloc = new_request_alloc; 533 kr = vm_allocate(self, 534 (vm_address_t *)&bufRequest, 535 request_alloc, 536 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 537 if (kr != KERN_SUCCESS) { 538 vm_deallocate(self, 539 (vm_address_t)bufReply, 540 reply_alloc); 541 return kr; 542 } 543 } 544 545 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options, 546 0, request_size, rcv_name, 547 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 548 549 while (mr == MACH_MSG_SUCCESS) { 550 /* we have another request message */ 551 552 (void) (*demux)(&bufRequest->Head, &bufReply->Head); 553 554 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 555 if (bufReply->RetCode == MIG_NO_REPLY) 556 bufReply->Head.msgh_remote_port = MACH_PORT_NULL; 557 else if ((bufReply->RetCode != KERN_SUCCESS) && 558 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 559 /* destroy the request - but not the reply port */ 560 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; 561 mach_msg_destroy(&bufRequest->Head); 562 } 563 } 564 565 /* 566 * We don't want to block indefinitely because the client 567 * isn't receiving messages from the reply port. 568 * If we have a send-once right for the reply port, then 569 * this isn't a concern because the send won't block. 570 * If we have a send right, we need to use MACH_SEND_TIMEOUT. 571 * To avoid falling off the kernel's fast RPC path, 572 * we only supply MACH_SEND_TIMEOUT when absolutely necessary. 573 */ 574 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) { 575 if (request_alloc == reply_alloc) { 576 mig_reply_error_t *bufTemp; 577 578 mr = mach_msg( 579 &bufReply->Head, 580 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == 581 MACH_MSG_TYPE_MOVE_SEND_ONCE) ? 582 MACH_SEND_MSG|MACH_RCV_MSG|options : 583 MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|options, 584 bufReply->Head.msgh_size, request_size, rcv_name, 585 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 586 587 /* swap request and reply */ 588 bufTemp = bufRequest; 589 bufRequest = bufReply; 590 bufReply = bufTemp; 591 592 } else { 593 mr = mach_msg_overwrite( 594 &bufReply->Head, 595 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == 596 MACH_MSG_TYPE_MOVE_SEND_ONCE) ? 597 MACH_SEND_MSG|MACH_RCV_MSG|options : 598 MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|options, 599 bufReply->Head.msgh_size, request_size, rcv_name, 600 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL, 601 &bufRequest->Head, 0); 602 } 603 604 if ((mr != MACH_SEND_INVALID_DEST) && 605 (mr != MACH_SEND_TIMED_OUT)) 606 continue; 607 } 608 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) 609 mach_msg_destroy(&bufReply->Head); 610 611 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options, 612 0, request_size, rcv_name, 613 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 614 615 } /* while (mr == MACH_MSG_SUCCESS) */ 616 617 if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) { 618 new_request_alloc = round_page(bufRequest->Head.msgh_size + 619 trailer_alloc); 620 request_size = new_request_alloc; 621 vm_deallocate(self, 622 (vm_address_t) bufRequest, 623 request_alloc); 624 continue; 625 } 626 627 break; 628 629 } /* for(;;) */ 630 631 (void)vm_deallocate(self, 632 (vm_address_t) bufRequest, 633 request_alloc); 634 (void)vm_deallocate(self, 635 (vm_address_t) bufReply, 636 reply_alloc); 637 return mr; 638} 639 640/* 641 * Routine: mach_msg_server_importance 642 * Purpose: 643 * A simple generic server function which handles importance 644 * promotion assertions for adaptive daemons. 645 */ 646mach_msg_return_t 647mach_msg_server_importance( 648 boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *), 649 mach_msg_size_t max_size, 650 mach_port_t rcv_name, 651 mach_msg_options_t options) 652{ 653 mig_reply_error_t *bufRequest, *bufReply; 654 mach_msg_size_t request_size; 655 mach_msg_size_t new_request_alloc; 656 mach_msg_size_t request_alloc; 657 mach_msg_size_t trailer_alloc; 658 mach_msg_size_t reply_alloc; 659 mach_msg_return_t mr; 660 kern_return_t kr; 661 mach_port_t self = mach_task_self_; 662 int retval = 1; 663 uint64_t token; 664 665 options &= ~(MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_OVERWRITE); 666 667 reply_alloc = round_page((options & MACH_SEND_TRAILER) ? 668 (max_size + MAX_TRAILER_SIZE) : max_size); 669 670 kr = vm_allocate(self, 671 (vm_address_t *)&bufReply, 672 reply_alloc, 673 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 674 if (kr != KERN_SUCCESS) 675 return kr; 676 677 request_alloc = 0; 678 trailer_alloc = REQUESTED_TRAILER_SIZE(options); 679 new_request_alloc = round_page(max_size + trailer_alloc); 680 681 request_size = (options & MACH_RCV_LARGE) ? 682 new_request_alloc : max_size + trailer_alloc; 683 684 for (;;) { 685 if (request_alloc < new_request_alloc) { 686 request_alloc = new_request_alloc; 687 kr = vm_allocate(self, 688 (vm_address_t *)&bufRequest, 689 request_alloc, 690 VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE); 691 if (kr != KERN_SUCCESS) { 692 vm_deallocate(self, 693 (vm_address_t)bufReply, 694 reply_alloc); 695 return kr; 696 } 697 } 698 699 mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|options, 700 0, request_size, rcv_name, 701 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 702 703 if (mr == MACH_MSG_SUCCESS) { 704 /* we have another request message */ 705 706 retval = proc_importance_assertion_begin_with_msg(&bufRequest->Head, NULL, &token); 707 (void) (*demux)(&bufRequest->Head, &bufReply->Head); 708 709 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 710 if (bufReply->RetCode == MIG_NO_REPLY) 711 bufReply->Head.msgh_remote_port = MACH_PORT_NULL; 712 else if ((bufReply->RetCode != KERN_SUCCESS) && 713 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { 714 /* destroy the request - but not the reply port */ 715 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; 716 mach_msg_destroy(&bufRequest->Head); 717 } 718 } 719 720 /* 721 * We don't want to block indefinitely because the client 722 * isn't receiving messages from the reply port. 723 * If we have a send-once right for the reply port, then 724 * this isn't a concern because the send won't block. 725 * If we have a send right, we need to use MACH_SEND_TIMEOUT. 726 * To avoid falling off the kernel's fast RPC path, 727 * we only supply MACH_SEND_TIMEOUT when absolutely necessary. 728 */ 729 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) { 730 731 mr = mach_msg( 732 &bufReply->Head, 733 (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) == 734 MACH_MSG_TYPE_MOVE_SEND_ONCE) ? 735 MACH_SEND_MSG|options : 736 MACH_SEND_MSG|MACH_SEND_TIMEOUT|options, 737 bufReply->Head.msgh_size, 0, MACH_PORT_NULL, 738 MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); 739 740 if ((mr != MACH_SEND_INVALID_DEST) && 741 (mr != MACH_SEND_TIMED_OUT)) { 742 if (retval == 0) 743 proc_importance_assertion_complete(token); 744 continue; 745 } 746 mr = MACH_MSG_SUCCESS; 747 } 748 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) 749 mach_msg_destroy(&bufReply->Head); 750 if (retval == 0) 751 proc_importance_assertion_complete(token); 752 753 } /* if (mr == MACH_MSG_SUCCESS) */ 754 755 if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) { 756 new_request_alloc = round_page(bufRequest->Head.msgh_size + 757 trailer_alloc); 758 request_size = new_request_alloc; 759 vm_deallocate(self, 760 (vm_address_t) bufRequest, 761 request_alloc); 762 continue; 763 } else if (mr == MACH_MSG_SUCCESS) 764 continue; 765 else 766 break; 767 768 } /* for(;;) */ 769 770 (void)vm_deallocate(self, 771 (vm_address_t) bufRequest, 772 request_alloc); 773 (void)vm_deallocate(self, 774 (vm_address_t) bufReply, 775 reply_alloc); 776 return mr; 777} 778