X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/378393581903b274cb7a4d18e0d978071a6b592d..4a3eedf9ecc9bbe3f3a5c6ce5e53ad199d639d32:/osfmk/ipc/mach_msg.c diff --git a/osfmk/ipc/mach_msg.c b/osfmk/ipc/mach_msg.c index f532b5230..065f3b40f 100644 --- a/osfmk/ipc/mach_msg.c +++ b/osfmk/ipc/mach_msg.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2007 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ @@ -47,6 +53,13 @@ * any improvements or extensions that they make and grant Carnegie Mellon * the rights to redistribute these changes. */ +/* + * NOTICE: This file was modified by McAfee Research in 2004 to introduce + * support for mandatory and extensible security protections. This notice + * is included in support of clause 2.2 (b) of the Apple Public License, + * Version 2.0. + * Copyright (c) 2005 SPARTA, Inc. + */ /* */ /* @@ -93,9 +106,11 @@ #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 /* offsetof */ @@ -183,6 +198,9 @@ mach_msg_send( 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); @@ -265,7 +283,7 @@ mach_msg_receive_results(void) ipc_kmsg_t kmsg = self->ith_kmsg; mach_port_seqno_t seqno = self->ith_seqno; - mach_msg_format_0_trailer_t *trailer; + mach_msg_max_trailer_t *trailer; ipc_object_release(object); @@ -293,12 +311,63 @@ mach_msg_receive_results(void) goto out; } - trailer = (mach_msg_format_0_trailer_t *) + 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_trailer_size = REQUESTED_TRAILER_SIZE(option); + + + if (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, + &((ipc_port_t)kmsg->ikm_header->msgh_remote_port)->ip_label, + kmsg->ikm_header->msgh_id) == 0) + trailer->msgh_ad = 1; + else +#endif + trailer->msgh_ad = 0; + } + + /* + * The ipc_kmsg_t holds a reference to the label of a label + * handle, not the port. We must get a reference to the port + * and a send right to copyout to the receiver. + */ + + 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 + } } /* @@ -556,7 +625,11 @@ unsigned int c_mmot_kernel_send = 0; /* kernel server calls */ #endif /* !HOTPATH_DEBUG */ +#if CONFIG_MACF_MACH +boolean_t enable_hotpath = FALSE; /* XXX - push MAC into HOTPATH too */ +#else boolean_t enable_hotpath = TRUE; /* Patchable, just in case ... */ +#endif #endif /* HOTPATH_ENABLE */ /* @@ -582,15 +655,15 @@ mach_msg_overwrite_trap( 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; /* NOT INITIALIZED - but not used in pactice */ + __unused mach_port_seqno_t temp_seqno = 0; - register mach_msg_header_t *hdr; mach_msg_return_t mr = MACH_MSG_SUCCESS; +#if ENABLE_HOTPATH /* mask out some of the options before entering the hot path */ mach_msg_option_t masked_option = option & ~(MACH_SEND_TRAILER|MACH_RCV_TRAILER_MASK|MACH_RCV_LARGE); + register mach_msg_header_t *hdr; -#if ENABLE_HOTPATH - /* BEGINNING OF HOT PATH */ if ((masked_option == (MACH_SEND_MSG|MACH_RCV_MSG)) && enable_hotpath) { thread_t self = current_thread(); mach_msg_format_0_trailer_t *trailer; @@ -1088,119 +1161,88 @@ mach_msg_overwrite_trap( register ipc_mqueue_t dest_mqueue; wait_queue_t waitq; thread_t receiver; - processor_t processor; - boolean_t still_running; spl_t s; s = splsched(); - processor = current_processor(); - if (processor->current_pri >= BASEPRI_RTQUEUES) - goto abort_send_receive1; - dest_mqueue = &dest_port->ip_messages; waitq = &dest_mqueue->imq_wait_queue; imq_lock(dest_mqueue); - wait_queue_peek64_locked(waitq, IPC_MQUEUE_RECEIVE, &receiver, &waitq); - /* queue still locked, thread locked - but still on q */ + get_next_receiver: + receiver = wait_queue_wakeup64_identity_locked(waitq, + IPC_MQUEUE_RECEIVE, + THREAD_AWAKENED, + FALSE); + /* queue still locked, receiver thread locked (if any) */ if ( receiver == THREAD_NULL ) { - abort_send_receive: - imq_unlock(dest_mqueue); - abort_send_receive1: - splx(s); - ip_unlock(dest_port); - ipc_object_release(rcv_object); - HOT(c_mmot_cold_032++); - goto slow_send; - } - - assert(receiver->state & TH_WAIT); - assert(receiver->wait_queue == waitq); - assert(receiver->wait_event == IPC_MQUEUE_RECEIVE); - - /* - * Make sure that the scheduling restrictions of the receiver - * are consistent with a handoff here (if it comes down to that). - */ - if ( receiver->sched_pri >= BASEPRI_RTQUEUES || - receiver->processor_set != processor->processor_set || - (receiver->bound_processor != PROCESSOR_NULL && - receiver->bound_processor != processor)) { - HOT(c_mmot_cold_033++); - fall_off: - thread_unlock(receiver); - if (waitq != &dest_mqueue->imq_wait_queue) - wait_queue_unlock(waitq); - goto abort_send_receive; - } + imq_unlock(dest_mqueue); + splx(s); - /* - * Check that the receiver can stay on the hot path. - */ - if (ipc_kmsg_copyout_size(kmsg, receiver->map) + - REQUESTED_TRAILER_SIZE(receiver->ith_option) > receiver->ith_msize) { - /* - * The receiver can't accept the message. - */ - HOT(c_mmot_bad_rcvr++); - goto fall_off; + ip_unlock(dest_port); + ipc_object_release(rcv_object); + HOT(c_mmot_cold_032++); + goto slow_send; } /* - * Before committing to the handoff, make sure that we are - * really going to block (i.e. there are no messages already - * queued for us. This violates lock ordering, so make sure - * we don't deadlock. After the trylock succeeds below, we - * may have up to 3 message queues locked: - * - the dest port mqueue - * - a portset mqueue (where waiting receiver was found) - * - finally our own rcv_mqueue + * Check that the receiver can handle the size of the message. + * If not, and the receiver just wants to be informed of that + * fact, set it running and try to find another thread. * - * JMM - Need to make this check appropriate for portsets as - * well before re-enabling them. + * If he didn't want the "too large" message left on the queue, + * give it to him anyway, he'll consume it as part of his receive + * processing. */ - if (!imq_lock_try(rcv_mqueue)) { - goto fall_off; - } - if (ipc_kmsg_queue_first(&rcv_mqueue->imq_messages) != IKM_NULL) { - imq_unlock(rcv_mqueue); - HOT(c_mmot_cold_033++); - goto fall_off; + if (receiver->ith_msize < + ipc_kmsg_copyout_size(kmsg, receiver->map) + + REQUESTED_TRAILER_SIZE(receiver->ith_option)) + { + receiver->ith_msize = kmsg->ikm_header->msgh_size; + receiver->ith_state = MACH_RCV_TOO_LARGE; + + if ((receiver->ith_option & MACH_RCV_LARGE) != 0) { + receiver->ith_kmsg = IKM_NULL; + receiver->ith_seqno = 0; + thread_unlock(receiver); + HOT(c_mmot_bad_rcvr++); + goto get_next_receiver; + } + } else { + receiver->ith_state = MACH_MSG_SUCCESS; } - /* At this point we are committed to do the "handoff". */ + /* At this point we are committed to do the message handoff. */ c_mach_msg_trap_switch_fast++; - - /* - * Go ahead and pull the receiver from the waitq. If the - * waitq wasn't the one for the mqueue, unlock it. - */ - wait_queue_pull_thread_locked(waitq, - receiver, - (waitq != &dest_mqueue->imq_wait_queue)); /* * Store the kmsg and seqno where the receiver can pick it up. + * and set it running. */ - receiver->ith_state = MACH_MSG_SUCCESS; receiver->ith_kmsg = kmsg; receiver->ith_seqno = dest_mqueue->imq_seqno++; - - /* - * Unblock the receiver. If it was still running on another - * CPU, we'll give it a chance to run with the message where - * it is (and just select someother thread to run here). - * Otherwise, we'll invoke it here as part of the handoff. - */ - still_running = thread_unblock(receiver, THREAD_AWAKENED); - thread_unlock(receiver); imq_unlock(dest_mqueue); ip_unlock(dest_port); current_task()->messages_sent++; + + /* + * Now prepare to wait on our receive queue. But we have to make + * sure the queue doesn't already have messages. If it does, we'll + * have to do a slow receive. + * + * JMM - Need to make this check appropriate for portsets as + * well before re-enabling them. + */ + imq_lock(rcv_mqueue); + if (ipc_kmsg_queue_first(&rcv_mqueue->imq_messages) != IKM_NULL) { + imq_unlock(rcv_mqueue); + splx(s); + HOT(c_mmot_cold_033++); + goto slow_receive; + } /* * Put self on receive port's queue. @@ -1223,19 +1265,8 @@ mach_msg_overwrite_trap( self); thread_unlock(self); imq_unlock(rcv_mqueue); - - /* - * If the receiving thread wasn't still running, we switch directly - * to it here. Otherwise we let the scheduler pick something for - * here. In either case, block this thread as though it had called - * ipc_mqueue_receive. - */ - if (still_running) { - splx(s); - thread_block(ipc_mqueue_receive_continue); - } else { - thread_run(self, ipc_mqueue_receive_continue, NULL, receiver); - } + splx(s); + thread_block(ipc_mqueue_receive_continue); /* NOTREACHED */ } @@ -1488,7 +1519,6 @@ mach_msg_overwrite_trap( slow_copyin: { - mach_port_seqno_t temp_seqno = 0; register mach_port_name_t reply_name = (mach_port_name_t)hdr->msgh_local_port; @@ -1581,7 +1611,6 @@ mach_msg_overwrite_trap( { register ipc_port_t reply_port; - mach_port_seqno_t local_seqno; spl_t s; /* @@ -1658,7 +1687,7 @@ mach_msg_overwrite_trap( * no threads blocked waiting to send. */ dest_port = reply_port; - local_seqno = rcv_mqueue->imq_seqno++; + temp_seqno = rcv_mqueue->imq_seqno++; imq_unlock(rcv_mqueue); splx(s); @@ -1670,7 +1699,7 @@ mach_msg_overwrite_trap( ip_check_unlock(reply_port); if (option & MACH_RCV_TRAILER_MASK) { - trailer->msgh_seqno = local_seqno; + trailer->msgh_seqno = temp_seqno; trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option); } /* copy out the kernel reply */ @@ -1708,13 +1737,13 @@ mach_msg_overwrite_trap( /* hold ref for rcv_object */ /* - * slow_receive: * * Now we have sent the request and copied in rcv_name, * and hold ref for rcv_object (to keep mqueue alive). * Just receive a reply and try to get back to fast path. */ + slow_receive: self->ith_continuation = (void (*)(mach_msg_return_t))0; ipc_mqueue_receive(rcv_mqueue, MACH_MSG_OPTION_NONE, @@ -1727,22 +1756,22 @@ mach_msg_overwrite_trap( ipc_object_release(rcv_object); - if (mr != MACH_MSG_SUCCESS) { + if (mr != MACH_MSG_SUCCESS) { return(mr); - } + } - kmsg = self->ith_kmsg; - hdr = kmsg->ikm_header; - send_size = hdr->msgh_size; - trailer = (mach_msg_format_0_trailer_t *) ((vm_offset_t) hdr + - round_msg(send_size)); - if (option & MACH_RCV_TRAILER_MASK) { + kmsg = self->ith_kmsg; + hdr = kmsg->ikm_header; + send_size = hdr->msgh_size; + trailer = (mach_msg_format_0_trailer_t *) ((vm_offset_t) hdr + + round_msg(send_size)); + if (option & MACH_RCV_TRAILER_MASK) { trailer->msgh_seqno = temp_seqno; trailer->msgh_trailer_size = REQUESTED_TRAILER_SIZE(option); - } - dest_port = (ipc_port_t) hdr->msgh_remote_port; - HOT(c_mmot_cold_055++); - goto fast_copyout; + } + dest_port = (ipc_port_t) hdr->msgh_remote_port; + HOT(c_mmot_cold_055++); + goto fast_copyout; slow_copyout: /* @@ -1755,6 +1784,7 @@ mach_msg_overwrite_trap( /* LP64support - have to compute real size as it would be received */ reply_size = ipc_kmsg_copyout_size(kmsg, current_map()) + REQUESTED_TRAILER_SIZE(option); + temp_seqno = trailer->msgh_seqno; if (rcv_size < reply_size) { if (msg_receive_error(kmsg, msg_addr, option, temp_seqno, space) == MACH_RCV_INVALID_DATA) {