#include <mach/mach_types.h>
#include <mach/kern_return.h>
#include <mach/port.h>
#include <mach/message.h>
#include <mach/mig_errors.h>
#include <mach/mach_traps.h>
#include <kern/kern_types.h>
#include <kern/assert.h>
#include <kern/counters.h>
#include <kern/cpu_number.h>
#include <kern/ipc_kobject.h>
#include <kern/ipc_mig.h>
#include <kern/task.h>
#include <kern/thread.h>
#include <kern/lock.h>
#include <kern/sched_prim.h>
#include <kern/exception.h>
#include <kern/misc_protos.h>
#include <kern/kalloc.h>
#include <kern/processor.h>
#include <kern/syscall_subr.h>
#include <vm/vm_map.h>
#include <ipc/ipc_types.h>
#include <ipc/ipc_kmsg.h>
#include <ipc/ipc_mqueue.h>
#include <ipc/ipc_object.h>
#include <ipc/ipc_notify.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_pset.h>
#include <ipc/ipc_space.h>
#include <ipc/ipc_entry.h>
#include <machine/machine_routines.h>
#include <security/mac_mach_internal.h>
#include <sys/kdebug.h>
#ifndef offsetof
#define offsetof(type, member) ((size_t)(&((type *)0)->member))
#endif
mach_msg_return_t mach_msg_send(
mach_msg_header_t *msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_timeout_t send_timeout,
mach_port_name_t notify);
mach_msg_return_t mach_msg_receive(
mach_msg_header_t *msg,
mach_msg_option_t option,
mach_msg_size_t rcv_size,
mach_port_name_t rcv_name,
mach_msg_timeout_t rcv_timeout,
void (*continuation)(mach_msg_return_t),
mach_msg_size_t slist_size);
mach_msg_return_t msg_receive_error(
ipc_kmsg_t kmsg,
mach_vm_address_t msg_addr,
mach_msg_option_t option,
mach_port_seqno_t seqno,
ipc_space_t space);
security_token_t KERNEL_SECURITY_TOKEN = KERNEL_SECURITY_TOKEN_VALUE;
audit_token_t KERNEL_AUDIT_TOKEN = KERNEL_AUDIT_TOKEN_VALUE;
mach_msg_format_0_trailer_t trailer_template = {
MACH_MSG_TRAILER_FORMAT_0,
MACH_MSG_TRAILER_MINIMUM_SIZE,
0,
KERNEL_SECURITY_TOKEN_VALUE
};
mach_msg_return_t
mach_msg_send(
mach_msg_header_t *msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_timeout_t send_timeout,
__unused mach_port_name_t notify)
{
ipc_space_t space = current_space();
vm_map_t map = current_map();
ipc_kmsg_t kmsg;
mach_msg_return_t mr;
mach_msg_size_t msg_and_trailer_size;
mach_msg_max_trailer_t *trailer;
if ((send_size < sizeof(mach_msg_header_t)) || (send_size & 3))
return MACH_SEND_MSG_TOO_SMALL;
if (send_size > MACH_MSG_SIZE_MAX - MAX_TRAILER_SIZE)
return MACH_SEND_TOO_LARGE;
msg_and_trailer_size = send_size + MAX_TRAILER_SIZE;
kmsg = ipc_kmsg_alloc(msg_and_trailer_size);
if (kmsg == IKM_NULL)
return MACH_SEND_NO_BUFFER;
(void) memcpy((void *) kmsg->ikm_header, (const void *) msg, send_size);
kmsg->ikm_header->msgh_size = send_size;
trailer = (mach_msg_max_trailer_t *) ((vm_offset_t)kmsg->ikm_header + send_size);
trailer->msgh_sender = current_thread()->task->sec_token;
trailer->msgh_audit = current_thread()->task->audit_token;
trailer->msgh_trailer_type = MACH_MSG_TRAILER_FORMAT_0;
trailer->msgh_trailer_size = MACH_MSG_TRAILER_MINIMUM_SIZE;
mr = ipc_kmsg_copyin(kmsg, space, map, option & MACH_SEND_NOTIFY);
if (mr != MACH_MSG_SUCCESS) {
ipc_kmsg_free(kmsg);
return mr;
}
mr = ipc_kmsg_send(kmsg, option & MACH_SEND_TIMEOUT, send_timeout);
if (mr != MACH_MSG_SUCCESS) {
mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map, MACH_MSG_BODY_NULL);
(void) memcpy((void *) msg, (const void *) kmsg->ikm_header,
kmsg->ikm_header->msgh_size);
ipc_kmsg_free(kmsg);
}
return mr;
}
mach_msg_return_t
mach_msg_receive_results(void)
{
thread_t self = current_thread();
ipc_space_t space = current_space();
vm_map_t map = current_map();
ipc_object_t object = self->ith_object;
mach_msg_return_t mr = self->ith_state;
mach_vm_address_t msg_addr = self->ith_msg_addr;
mach_msg_option_t option = self->ith_option;
ipc_kmsg_t kmsg = self->ith_kmsg;
mach_port_seqno_t seqno = self->ith_seqno;
mach_msg_max_trailer_t *trailer;
ipc_object_release(object);
if (mr != MACH_MSG_SUCCESS) {
if (mr == MACH_RCV_TOO_LARGE ) {
if (option & MACH_RCV_LARGE) {
if (copyout((char *) &self->ith_msize,
msg_addr + offsetof(mach_msg_header_t, msgh_size),
sizeof(mach_msg_size_t)))
mr = MACH_RCV_INVALID_DATA;
goto out;
}
if (msg_receive_error(kmsg, msg_addr, option, seqno, space)
== MACH_RCV_INVALID_DATA)
mr = MACH_RCV_INVALID_DATA;
}
goto out;
}
trailer = (mach_msg_max_trailer_t *)
((vm_offset_t)kmsg->ikm_header +
round_msg(kmsg->ikm_header->msgh_size));
if (option & MACH_RCV_TRAILER_MASK) {
trailer->msgh_seqno = seqno;
trailer->msgh_context =
kmsg->ikm_header->msgh_remote_port->ip_context;
trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option);
if (MACH_RCV_TRAILER_ELEMENTS(option) >=
MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV)){
#if CONFIG_MACF_MACH
if (kmsg->ikm_sender != NULL &&
IP_VALID(kmsg->ikm_header->msgh_remote_port) &&
mac_port_check_method(kmsg->ikm_sender,
&kmsg->ikm_sender->maclabel,
&kmsg->ikm_header->msgh_remote_port->ip_label,
kmsg->ikm_header->msgh_id) == 0)
trailer->msgh_ad = 1;
else
#endif
trailer->msgh_ad = 0;
}
if (option & MACH_RCV_TRAILER_ELEMENTS (MACH_RCV_TRAILER_LABELS)) {
#if CONFIG_MACF_MACH
if (kmsg->ikm_sender != NULL) {
ipc_labelh_t lh = kmsg->ikm_sender->label;
kern_return_t kr;
ip_lock(lh->lh_port);
lh->lh_port->ip_mscount++;
lh->lh_port->ip_srights++;
ip_reference(lh->lh_port);
ip_unlock(lh->lh_port);
kr = ipc_object_copyout(space, (ipc_object_t)lh->lh_port,
MACH_MSG_TYPE_PORT_SEND, 0,
&trailer->msgh_labels.sender);
if (kr != KERN_SUCCESS) {
ip_lock(lh->lh_port);
ip_release(lh->lh_port);
ip_check_unlock(lh->lh_port);
trailer->msgh_labels.sender = 0;
}
} else {
trailer->msgh_labels.sender = 0;
}
#else
trailer->msgh_labels.sender = 0;
#endif
}
}
if (option & MACH_RCV_OVERWRITE) {
mach_msg_size_t slist_size = self->ith_scatter_list_size;
mach_msg_body_t *slist;
slist = ipc_kmsg_get_scatter(msg_addr, slist_size, kmsg);
mr = ipc_kmsg_copyout(kmsg, space, map, slist);
ipc_kmsg_free_scatter(slist, slist_size);
} else {
mr = ipc_kmsg_copyout(kmsg, space, map, MACH_MSG_BODY_NULL);
}
if (mr != MACH_MSG_SUCCESS) {
if ((mr &~ MACH_MSG_MASK) == MACH_RCV_BODY_ERROR) {
if (ipc_kmsg_put(msg_addr, kmsg, kmsg->ikm_header->msgh_size +
trailer->msgh_trailer_size) == MACH_RCV_INVALID_DATA)
mr = MACH_RCV_INVALID_DATA;
}
else {
if (msg_receive_error(kmsg, msg_addr, option, seqno, space)
== MACH_RCV_INVALID_DATA)
mr = MACH_RCV_INVALID_DATA;
}
goto out;
}
mr = ipc_kmsg_put(msg_addr,
kmsg,
kmsg->ikm_header->msgh_size +
trailer->msgh_trailer_size);
out:
return mr;
}
mach_msg_return_t
mach_msg_receive(
mach_msg_header_t *msg,
mach_msg_option_t option,
mach_msg_size_t rcv_size,
mach_port_name_t rcv_name,
mach_msg_timeout_t rcv_timeout,
void (*continuation)(mach_msg_return_t),
mach_msg_size_t slist_size)
{
thread_t self = current_thread();
ipc_space_t space = current_space();
ipc_object_t object;
ipc_mqueue_t mqueue;
mach_msg_return_t mr;
mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
if (mr != MACH_MSG_SUCCESS) {
return mr;
}
self->ith_msg_addr = CAST_DOWN(mach_vm_address_t, msg);
self->ith_object = object;
self->ith_msize = rcv_size;
self->ith_option = option;
self->ith_scatter_list_size = slist_size;
self->ith_continuation = continuation;
ipc_mqueue_receive(mqueue, option, rcv_size, rcv_timeout, THREAD_ABORTSAFE);
if ((option & MACH_RCV_TIMEOUT) && rcv_timeout == 0)
thread_poll_yield(self);
return mach_msg_receive_results();
}
void
mach_msg_receive_continue(void)
{
thread_t self = current_thread();
(*self->ith_continuation)(mach_msg_receive_results());
}
mach_msg_return_t
mach_msg_overwrite_trap(
struct mach_msg_overwrite_trap_args *args)
{
mach_vm_address_t msg_addr = args->msg;
mach_msg_option_t option = args->option;
mach_msg_size_t send_size = args->send_size;
mach_msg_size_t rcv_size = args->rcv_size;
mach_port_name_t rcv_name = args->rcv_name;
mach_msg_timeout_t msg_timeout = args->timeout;
__unused mach_port_name_t notify = args->notify;
mach_vm_address_t rcv_msg_addr = args->rcv_msg;
mach_msg_size_t scatter_list_size = 0;
__unused mach_port_seqno_t temp_seqno = 0;
mach_msg_return_t mr = MACH_MSG_SUCCESS;
vm_map_t map = current_map();
if (option & MACH_SEND_MSG) {
ipc_space_t space = current_space();
ipc_kmsg_t kmsg;
mr = ipc_kmsg_get(msg_addr, send_size, &kmsg);
if (mr != MACH_MSG_SUCCESS)
return mr;
mr = ipc_kmsg_copyin(kmsg, space, map, option & MACH_SEND_NOTIFY);
if (mr != MACH_MSG_SUCCESS) {
ipc_kmsg_free(kmsg);
return mr;
}
mr = ipc_kmsg_send(kmsg, option & MACH_SEND_TIMEOUT, msg_timeout);
if (mr != MACH_MSG_SUCCESS) {
mr |= ipc_kmsg_copyout_pseudo(kmsg, space, map, MACH_MSG_BODY_NULL);
(void) ipc_kmsg_put(msg_addr, kmsg, kmsg->ikm_header->msgh_size);
return mr;
}
}
if (option & MACH_RCV_MSG) {
thread_t self = current_thread();
ipc_space_t space = current_space();
ipc_object_t object;
ipc_mqueue_t mqueue;
mr = ipc_mqueue_copyin(space, rcv_name, &mqueue, &object);
if (mr != MACH_MSG_SUCCESS) {
return mr;
}
if (option & MACH_RCV_OVERWRITE)
self->ith_msg_addr = rcv_msg_addr;
else if (rcv_msg_addr != (mach_vm_address_t)0)
self->ith_msg_addr = rcv_msg_addr;
else
self->ith_msg_addr = msg_addr;
self->ith_object = object;
self->ith_msize = rcv_size;
self->ith_option = option;
self->ith_scatter_list_size = scatter_list_size;
self->ith_receiver_name = MACH_PORT_NULL;
self->ith_continuation = thread_syscall_return;
ipc_mqueue_receive(mqueue, option, rcv_size, msg_timeout, THREAD_ABORTSAFE);
if ((option & MACH_RCV_TIMEOUT) && msg_timeout == 0)
thread_poll_yield(self);
return mach_msg_receive_results();
}
return MACH_MSG_SUCCESS;
}
mach_msg_return_t
mach_msg_trap(
struct mach_msg_overwrite_trap_args *args)
{
kern_return_t kr;
args->rcv_msg = (mach_vm_address_t)0;
kr = mach_msg_overwrite_trap(args);
return kr;
}
mach_msg_return_t
msg_receive_error(
ipc_kmsg_t kmsg,
mach_vm_address_t msg_addr,
mach_msg_option_t option,
mach_port_seqno_t seqno,
ipc_space_t space)
{
mach_msg_max_trailer_t *trailer;
mach_vm_address_t context;
context = kmsg->ikm_header->msgh_remote_port->ip_context;
ipc_kmsg_copyout_dest(kmsg, space);
trailer = (mach_msg_max_trailer_t *)
((vm_offset_t)kmsg->ikm_header +
round_msg(sizeof(mach_msg_header_t)));
kmsg->ikm_header->msgh_size = sizeof(mach_msg_header_t);
bcopy( (char *)&trailer_template,
(char *)trailer,
sizeof(trailer_template));
if (option & MACH_RCV_TRAILER_MASK) {
trailer->msgh_context = context;
trailer->msgh_seqno = seqno;
trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option);
}
if (ipc_kmsg_put(msg_addr, kmsg, kmsg->ikm_header->msgh_size +
trailer->msgh_trailer_size) == MACH_RCV_INVALID_DATA)
return(MACH_RCV_INVALID_DATA);
else
return(MACH_MSG_SUCCESS);
}