#include <stdlib.h>
#include <mach/mach.h>
#include <mach/boolean.h>
#include <mach/kern_return.h>
#include <mach/message.h>
#include <mach/mig_errors.h>
#include <mach/vm_statistics.h>
#include <TargetConditionals.h>
extern int proc_importance_assertion_begin_with_msg(mach_msg_header_t * msg, mach_msg_trailer_t * trailer, uint64_t * assertion_handlep);
extern int proc_importance_assertion_complete(uint64_t assertion_handle);
#define MACH_MSG_TRAP(msg, opt, ssize, rsize, rname, to, not) \
mach_msg_trap((msg), (opt), (ssize), (rsize), (rname), (to), (not))
#define LIBMACH_OPTIONS (MACH_SEND_INTERRUPT|MACH_RCV_INTERRUPT)
mach_msg_return_t
mach_msg(msg, option, send_size, rcv_size, rcv_name, timeout, notify)
mach_msg_header_t *msg;
mach_msg_option_t option;
mach_msg_size_t send_size;
mach_msg_size_t rcv_size;
mach_port_t rcv_name;
mach_msg_timeout_t timeout;
mach_port_t notify;
{
mach_msg_return_t mr;
mr = MACH_MSG_TRAP(msg, option &~ LIBMACH_OPTIONS,
send_size, rcv_size, rcv_name,
timeout, notify);
if (mr == MACH_MSG_SUCCESS)
return MACH_MSG_SUCCESS;
if ((option & MACH_SEND_INTERRUPT) == 0)
while (mr == MACH_SEND_INTERRUPTED)
mr = MACH_MSG_TRAP(msg,
option &~ LIBMACH_OPTIONS,
send_size, rcv_size, rcv_name,
timeout, notify);
if ((option & MACH_RCV_INTERRUPT) == 0)
while (mr == MACH_RCV_INTERRUPTED)
mr = MACH_MSG_TRAP(msg,
option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG),
0, rcv_size, rcv_name,
timeout, notify);
return mr;
}
mach_msg_return_t
mach_msg_overwrite(msg, option, send_size, rcv_limit, rcv_name, timeout,
notify, rcv_msg, rcv_scatter_size)
mach_msg_header_t *msg;
mach_msg_option_t option;
mach_msg_size_t send_size;
mach_msg_size_t rcv_limit;
mach_port_t rcv_name;
mach_msg_timeout_t timeout;
mach_port_t notify;
mach_msg_header_t *rcv_msg;
mach_msg_size_t rcv_scatter_size;
{
mach_msg_return_t mr;
mr = mach_msg_overwrite_trap(msg, option &~ LIBMACH_OPTIONS,
send_size, rcv_limit, rcv_name,
timeout, notify, rcv_msg, rcv_scatter_size);
if (mr == MACH_MSG_SUCCESS)
return MACH_MSG_SUCCESS;
if ((option & MACH_SEND_INTERRUPT) == 0)
while (mr == MACH_SEND_INTERRUPTED)
mr = mach_msg_overwrite_trap(msg,
option &~ LIBMACH_OPTIONS,
send_size, rcv_limit, rcv_name,
timeout, notify, rcv_msg, rcv_scatter_size);
if ((option & MACH_RCV_INTERRUPT) == 0)
while (mr == MACH_RCV_INTERRUPTED)
mr = mach_msg_overwrite_trap(msg,
option &~ (LIBMACH_OPTIONS|MACH_SEND_MSG),
0, rcv_limit, rcv_name,
timeout, notify, rcv_msg, rcv_scatter_size);
return mr;
}
mach_msg_return_t
mach_msg_send(mach_msg_header_t *msg)
{
return mach_msg(msg, MACH_SEND_MSG,
msg->msgh_size, 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
mach_msg_return_t
mach_msg_receive(mach_msg_header_t *msg)
{
return mach_msg(msg, MACH_RCV_MSG,
0, msg->msgh_size, msg->msgh_local_port,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
static void
mach_msg_destroy_port(mach_port_t port, mach_msg_type_name_t type)
{
if (MACH_PORT_VALID(port)) switch (type) {
case MACH_MSG_TYPE_MOVE_SEND:
case MACH_MSG_TYPE_MOVE_SEND_ONCE:
(void) mach_port_deallocate(mach_task_self_, port);
break;
case MACH_MSG_TYPE_MOVE_RECEIVE:
(void) mach_port_mod_refs(mach_task_self_, port,
MACH_PORT_RIGHT_RECEIVE, -1);
break;
case MACH_MSG_TYPE_MAKE_SEND:
(void) mach_port_insert_right(mach_task_self_, port,
port, MACH_MSG_TYPE_MAKE_SEND);
(void) mach_port_deallocate(mach_task_self_, port);
break;
case MACH_MSG_TYPE_MAKE_SEND_ONCE:
(void) mach_port_extract_right(mach_task_self_, port,
MACH_MSG_TYPE_MAKE_SEND_ONCE,
&port, &type);
(void) mach_port_deallocate(mach_task_self_, port);
break;
}
}
static void
mach_msg_destroy_memory(vm_offset_t addr, vm_size_t size)
{
if (size != 0)
(void) vm_deallocate(mach_task_self_, addr, size);
}
void
mach_msg_destroy(mach_msg_header_t *msg)
{
mach_msg_bits_t mbits = msg->msgh_bits;
mach_msg_destroy_port(msg->msgh_remote_port, MACH_MSGH_BITS_REMOTE(mbits));
mach_msg_destroy_port(msg->msgh_voucher_port, MACH_MSGH_BITS_VOUCHER(mbits));
if (mbits & MACH_MSGH_BITS_COMPLEX) {
mach_msg_base_t *base;
mach_msg_type_number_t count, i;
mach_msg_descriptor_t *daddr;
base = (mach_msg_base_t *) msg;
count = base->body.msgh_descriptor_count;
daddr = (mach_msg_descriptor_t *) (base + 1);
for (i = 0; i < count; i++) {
switch (daddr->type.type) {
case MACH_MSG_PORT_DESCRIPTOR: {
mach_msg_port_descriptor_t *dsc;
dsc = &daddr->port;
mach_msg_destroy_port(dsc->name, dsc->disposition);
daddr = (mach_msg_descriptor_t *)(dsc + 1);
break;
}
case MACH_MSG_OOL_DESCRIPTOR: {
mach_msg_ool_descriptor_t *dsc;
dsc = &daddr->out_of_line;
if (dsc->deallocate) {
mach_msg_destroy_memory((vm_offset_t)dsc->address,
dsc->size);
}
daddr = (mach_msg_descriptor_t *)(dsc + 1);
break;
}
case MACH_MSG_OOL_VOLATILE_DESCRIPTOR: {
mach_msg_ool_descriptor_t *dsc;
dsc = &daddr->out_of_line;
daddr = (mach_msg_descriptor_t *)(dsc + 1);
break;
}
case MACH_MSG_OOL_PORTS_DESCRIPTOR: {
mach_port_t *ports;
mach_msg_ool_ports_descriptor_t *dsc;
mach_msg_type_number_t j;
dsc = &daddr->ool_ports;
ports = (mach_port_t *) dsc->address;
for (j = 0; j < dsc->count; j++, ports++) {
mach_msg_destroy_port(*ports, dsc->disposition);
}
if (dsc->deallocate) {
mach_msg_destroy_memory((vm_offset_t)dsc->address,
dsc->count * sizeof(mach_port_t));
}
daddr = (mach_msg_descriptor_t *)(dsc + 1);
break;
}
}
}
}
}
mach_msg_return_t
mach_msg_server_once(
boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
mach_msg_size_t max_size,
mach_port_t rcv_name,
mach_msg_options_t options)
{
mig_reply_error_t *bufRequest, *bufReply;
mach_msg_size_t request_size;
mach_msg_size_t request_alloc;
mach_msg_size_t trailer_alloc;
mach_msg_size_t reply_alloc;
mach_msg_return_t mr;
kern_return_t kr;
mach_port_t self = mach_task_self_;
voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
options &= ~(MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_VOUCHER);
trailer_alloc = REQUESTED_TRAILER_SIZE(options);
request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc);
request_size = (options & MACH_RCV_LARGE) ?
request_alloc : max_size + trailer_alloc;
reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ?
(max_size + MAX_TRAILER_SIZE) :
max_size);
kr = vm_allocate(self,
(vm_address_t *)&bufReply,
reply_alloc,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
if (kr != KERN_SUCCESS)
return kr;
for (;;) {
mach_msg_size_t new_request_alloc;
kr = vm_allocate(self,
(vm_address_t *)&bufRequest,
request_alloc,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
if (kr != KERN_SUCCESS) {
vm_deallocate(self,
(vm_address_t)bufReply,
reply_alloc);
return kr;
}
mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|MACH_RCV_VOUCHER|options,
0, request_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (!((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)))
break;
new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size +
trailer_alloc);
vm_deallocate(self,
(vm_address_t) bufRequest,
request_alloc);
request_size = request_alloc = new_request_alloc;
}
if (mr == MACH_MSG_SUCCESS) {
old_state = voucher_mach_msg_adopt(&bufRequest->Head);
(void) (*demux)(&bufRequest->Head, &bufReply->Head);
if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
if (bufReply->RetCode == MIG_NO_REPLY)
bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
else if ((bufReply->RetCode != KERN_SUCCESS) &&
(bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
mach_msg_destroy(&bufRequest->Head);
}
}
if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
mr = mach_msg(&bufReply->Head,
(MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
MACH_SEND_MSG|options :
MACH_SEND_MSG|MACH_SEND_TIMEOUT|options,
bufReply->Head.msgh_size, 0, MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if ((mr != MACH_SEND_INVALID_DEST) &&
(mr != MACH_SEND_TIMED_OUT))
goto done_once;
mr = MACH_MSG_SUCCESS;
}
if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
mach_msg_destroy(&bufReply->Head);
}
done_once:
voucher_mach_msg_revert(old_state);
(void)vm_deallocate(self,
(vm_address_t) bufRequest,
request_alloc);
(void)vm_deallocate(self,
(vm_address_t) bufReply,
reply_alloc);
return mr;
}
mach_msg_return_t
mach_msg_server(
boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
mach_msg_size_t max_size,
mach_port_t rcv_name,
mach_msg_options_t options)
{
mig_reply_error_t *bufRequest, *bufReply;
mach_msg_size_t request_size;
mach_msg_size_t new_request_alloc;
mach_msg_size_t request_alloc;
mach_msg_size_t trailer_alloc;
mach_msg_size_t reply_alloc;
mach_msg_return_t mr;
kern_return_t kr;
mach_port_t self = mach_task_self_;
voucher_mach_msg_state_t old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
boolean_t buffers_swapped = FALSE;
options &= ~(MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_VOUCHER|MACH_RCV_OVERWRITE);
reply_alloc = (mach_msg_size_t)round_page((options & MACH_SEND_TRAILER) ?
(max_size + MAX_TRAILER_SIZE) : max_size);
kr = vm_allocate(self,
(vm_address_t *)&bufReply,
reply_alloc,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
if (kr != KERN_SUCCESS)
return kr;
request_alloc = 0;
trailer_alloc = REQUESTED_TRAILER_SIZE(options);
new_request_alloc = (mach_msg_size_t)round_page(max_size + trailer_alloc);
request_size = (options & MACH_RCV_LARGE) ?
new_request_alloc : max_size + trailer_alloc;
for (;;) {
if (request_alloc < new_request_alloc) {
request_alloc = new_request_alloc;
kr = vm_allocate(self,
(vm_address_t *)&bufRequest,
request_alloc,
VM_MAKE_TAG(VM_MEMORY_MACH_MSG)|TRUE);
if (kr != KERN_SUCCESS) {
vm_deallocate(self,
(vm_address_t)bufReply,
reply_alloc);
return kr;
}
}
mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|MACH_RCV_VOUCHER|options,
0, request_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
while (mr == MACH_MSG_SUCCESS) {
buffers_swapped = FALSE;
old_state = voucher_mach_msg_adopt(&bufRequest->Head);
(void) (*demux)(&bufRequest->Head, &bufReply->Head);
if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
if (bufReply->RetCode == MIG_NO_REPLY)
bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
else if ((bufReply->RetCode != KERN_SUCCESS) &&
(bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
mach_msg_destroy(&bufRequest->Head);
}
}
if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
if (request_alloc == reply_alloc) {
mig_reply_error_t *bufTemp;
mr = mach_msg(
&bufReply->Head,
(MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options :
MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options,
bufReply->Head.msgh_size, request_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
bufTemp = bufRequest;
bufRequest = bufReply;
bufReply = bufTemp;
buffers_swapped = TRUE;
} else {
mr = mach_msg_overwrite(
&bufReply->Head,
(MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) ==
MACH_MSG_TYPE_MOVE_SEND_ONCE) ?
MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options :
MACH_SEND_MSG|MACH_RCV_MSG|MACH_SEND_TIMEOUT|MACH_RCV_TIMEOUT|MACH_RCV_VOUCHER|options,
bufReply->Head.msgh_size, request_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
&bufRequest->Head, 0);
}
if ((mr != MACH_SEND_INVALID_DEST) &&
(mr != MACH_SEND_TIMED_OUT) &&
(mr != MACH_RCV_TIMED_OUT)) {
voucher_mach_msg_revert(old_state);
old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
continue;
}
}
if (mr != MACH_RCV_TIMED_OUT) {
if (buffers_swapped) {
if (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
mach_msg_destroy(&bufRequest->Head);
} else {
if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)
mach_msg_destroy(&bufReply->Head);
}
}
voucher_mach_msg_revert(old_state);
old_state = VOUCHER_MACH_MSG_STATE_UNCHANGED;
mr = mach_msg(&bufRequest->Head, MACH_RCV_MSG|MACH_RCV_VOUCHER|options,
0, request_size, rcv_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
}
if ((mr == MACH_RCV_TOO_LARGE) && (options & MACH_RCV_LARGE)) {
new_request_alloc = (mach_msg_size_t)round_page(bufRequest->Head.msgh_size +
trailer_alloc);
request_size = new_request_alloc;
vm_deallocate(self,
(vm_address_t) bufRequest,
request_alloc);
continue;
}
break;
}
(void)vm_deallocate(self,
(vm_address_t) bufRequest,
request_alloc);
(void)vm_deallocate(self,
(vm_address_t) bufReply,
reply_alloc);
return mr;
}
mach_msg_return_t
mach_msg_server_importance(
boolean_t (*demux)(mach_msg_header_t *, mach_msg_header_t *),
mach_msg_size_t max_size,
mach_port_t rcv_name,
mach_msg_options_t options)
{
return mach_msg_server(demux, max_size, rcv_name, options);
}
kern_return_t
mach_voucher_deallocate(
mach_voucher_t voucher)
{
return mach_port_deallocate(mach_task_self(), voucher);
}