b5139de154
1) make sure Mach servers will not work on data beyond the end of the request message buffer. 2) make sure that on copying out the reply message buffer, we will not leak kernel data located after the buffer. 3) make sure that the server will not overwrite memory beyond the end of the reply message buffer. That check is the responsability of the server, there is just a DIAGNOSTIC test to check everything is in good shape. All currently implemented servers in NetBSD have been modified to check for this condition While we are here, build the mach services table (formerly in mach_namemap.c) and the services prototypes automatically from mach_services.master, just as this is done for system calls. The next step would be to fold the message formats in the mach_services.master file, but this tends to be difficult, as some messages are quite long and complex.
998 lines
26 KiB
C
998 lines
26 KiB
C
/* $NetBSD: mach_port.c,v 1.44 2003/11/13 13:40:39 manu Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 2002-2003 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Emmanuel Dreyfus
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "opt_compat_darwin.h"
|
|
|
|
#include <sys/cdefs.h>
|
|
__KERNEL_RCSID(0, "$NetBSD: mach_port.c,v 1.44 2003/11/13 13:40:39 manu Exp $");
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/signal.h>
|
|
#include <sys/pool.h>
|
|
#include <sys/queue.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/proc.h>
|
|
|
|
#include <compat/mach/mach_types.h>
|
|
#include <compat/mach/mach_message.h>
|
|
#include <compat/mach/mach_port.h>
|
|
#include <compat/mach/mach_iokit.h>
|
|
#include <compat/mach/mach_clock.h>
|
|
#include <compat/mach/mach_exec.h>
|
|
#include <compat/mach/mach_errno.h>
|
|
#include <compat/mach/mach_notify.h>
|
|
#include <compat/mach/mach_services.h>
|
|
#include <compat/mach/mach_syscallargs.h>
|
|
|
|
#ifdef COMPAT_DARWIN
|
|
#include <compat/darwin/darwin_exec.h>
|
|
#endif
|
|
|
|
/* Right and port pools, list of all rights and its lock */
|
|
static struct pool mach_port_pool;
|
|
static struct pool mach_right_pool;
|
|
|
|
struct mach_port *mach_bootstrap_port;
|
|
struct mach_port *mach_clock_port;
|
|
struct mach_port *mach_io_master_port;
|
|
struct mach_port *mach_saved_bootstrap_port;
|
|
|
|
int
|
|
mach_sys_reply_port(l, v, retval)
|
|
struct lwp *l;
|
|
void *v;
|
|
register_t *retval;
|
|
{
|
|
struct mach_right *mr;
|
|
|
|
mr = mach_right_get(mach_port_get(), l, MACH_PORT_TYPE_RECEIVE, 0);
|
|
*retval = (register_t)mr->mr_name;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mach_sys_thread_self_trap(l, v, retval)
|
|
struct lwp *l;
|
|
void *v;
|
|
register_t *retval;
|
|
{
|
|
struct mach_emuldata *med;
|
|
struct mach_right *mr;
|
|
|
|
/*
|
|
* XXX for now thread kernel port and task kernel port are the same
|
|
* awaiting for struct lwp ...
|
|
*/
|
|
med = (struct mach_emuldata *)l->l_proc->p_emuldata;
|
|
mr = mach_right_get(med->med_kernel, l, MACH_PORT_TYPE_SEND, 0);
|
|
*retval = (register_t)mr->mr_name;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
mach_sys_task_self_trap(l, v, retval)
|
|
struct lwp *l;
|
|
void *v;
|
|
register_t *retval;
|
|
{
|
|
struct mach_emuldata *med;
|
|
struct mach_right *mr;
|
|
|
|
med = (struct mach_emuldata *)l->l_proc->p_emuldata;
|
|
mr = mach_right_get(med->med_kernel, l, MACH_PORT_TYPE_SEND, 0);
|
|
*retval = (register_t)mr->mr_name;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
mach_sys_host_self_trap(l, v, retval)
|
|
struct lwp *l;
|
|
void *v;
|
|
register_t *retval;
|
|
{
|
|
struct mach_emuldata *med;
|
|
struct mach_right *mr;
|
|
|
|
med = (struct mach_emuldata *)l->l_proc->p_emuldata;
|
|
mr = mach_right_get(med->med_host, l, MACH_PORT_TYPE_SEND, 0);
|
|
*retval = (register_t)mr->mr_name;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mach_port_deallocate(args)
|
|
struct mach_trap_args *args;
|
|
{
|
|
mach_port_deallocate_request_t *req = args->smsg;
|
|
mach_port_deallocate_reply_t *rep = args->rmsg;
|
|
size_t *msglen = args->rsize;
|
|
struct lwp *l = args->l;
|
|
mach_port_t mn;
|
|
struct mach_right *mr;
|
|
|
|
mn = req->req_name;
|
|
if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_REF_RIGHTS)) != NULL)
|
|
mach_right_put(mr, MACH_PORT_TYPE_REF_RIGHTS);
|
|
|
|
rep->rep_msgh.msgh_bits =
|
|
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
|
|
rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer);
|
|
rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port;
|
|
rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100;
|
|
rep->rep_trailer.msgh_trailer_size = 8;
|
|
|
|
*msglen = sizeof(*rep);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mach_port_destroy(args)
|
|
struct mach_trap_args *args;
|
|
{
|
|
mach_port_destroy_request_t *req = args->smsg;
|
|
mach_port_destroy_reply_t *rep = args->rmsg;
|
|
size_t *msglen = args->rsize;
|
|
struct lwp *l = args->l;
|
|
mach_port_t mn;
|
|
struct mach_right *mr;
|
|
|
|
#ifdef DEBUG_MACH
|
|
printf("mach_port_destroy mn = %x\n", req->req_name);
|
|
#endif
|
|
mn = req->req_name;
|
|
if ((mr = mach_right_check(mn,
|
|
l, MACH_PORT_TYPE_ALL_RIGHTS)) != NULL) {
|
|
mr->mr_refcount = 0; /* Make sure it will be kicked away */
|
|
mach_right_put(mr, MACH_PORT_TYPE_ALL_RIGHTS);
|
|
}
|
|
|
|
rep->rep_msgh.msgh_bits =
|
|
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
|
|
rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer);
|
|
rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port;
|
|
rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100;
|
|
rep->rep_trailer.msgh_trailer_size = 8;
|
|
|
|
*msglen = sizeof(*rep);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mach_port_allocate(args)
|
|
struct mach_trap_args *args;
|
|
{
|
|
mach_port_allocate_request_t *req = args->smsg;
|
|
mach_port_allocate_reply_t *rep = args->rmsg;
|
|
size_t *msglen = args->rsize;
|
|
struct lwp *l = args->l;
|
|
struct mach_right *mr;
|
|
struct mach_port *mp;
|
|
|
|
switch (req->req_right) {
|
|
case MACH_PORT_RIGHT_RECEIVE:
|
|
mp = mach_port_get();
|
|
mr = mach_right_get(mp, l, MACH_PORT_TYPE_RECEIVE, 0);
|
|
break;
|
|
|
|
case MACH_PORT_RIGHT_DEAD_NAME:
|
|
mr = mach_right_get(NULL, l, MACH_PORT_TYPE_DEAD_NAME, 0);
|
|
break;
|
|
|
|
case MACH_PORT_RIGHT_PORT_SET:
|
|
mr = mach_right_get(NULL, l, MACH_PORT_TYPE_PORT_SET, 0);
|
|
break;
|
|
|
|
default:
|
|
uprintf("mach_port_allocate: unknown right %x\n",
|
|
req->req_right);
|
|
return mach_msg_error(args, EINVAL);
|
|
break;
|
|
}
|
|
|
|
rep->rep_msgh.msgh_bits =
|
|
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
|
|
rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer);
|
|
rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port;
|
|
rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100;
|
|
rep->rep_name = (mach_port_name_t)mr->mr_name;
|
|
rep->rep_trailer.msgh_trailer_size = 8;
|
|
|
|
*msglen = sizeof(*rep);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mach_port_insert_right(args)
|
|
struct mach_trap_args *args;
|
|
{
|
|
mach_port_insert_right_request_t *req = args->smsg;
|
|
mach_port_insert_right_reply_t *rep = args->rmsg;
|
|
size_t *msglen = args->rsize;
|
|
struct lwp *l = args->l;
|
|
mach_port_t name;
|
|
mach_port_t right;
|
|
struct mach_right *mr;
|
|
struct mach_right *nmr;
|
|
|
|
name = req->req_name;
|
|
right = req->req_poly.name;
|
|
nmr = NULL;
|
|
|
|
mr = mach_right_check(right, l, MACH_PORT_TYPE_ALL_RIGHTS);
|
|
if (mr == NULL)
|
|
return mach_msg_error(args, EPERM);
|
|
|
|
switch (req->req_poly.disposition) {
|
|
case MACH_MSG_TYPE_MAKE_SEND:
|
|
case MACH_MSG_TYPE_MOVE_SEND:
|
|
case MACH_MSG_TYPE_COPY_SEND:
|
|
nmr = mach_right_get(mr->mr_port,
|
|
l, MACH_PORT_TYPE_SEND, name);
|
|
break;
|
|
|
|
case MACH_MSG_TYPE_MAKE_SEND_ONCE:
|
|
case MACH_MSG_TYPE_MOVE_SEND_ONCE:
|
|
nmr = mach_right_get(mr->mr_port,
|
|
l, MACH_PORT_TYPE_SEND_ONCE, name);
|
|
break;
|
|
|
|
case MACH_MSG_TYPE_MOVE_RECEIVE:
|
|
nmr = mach_right_get(mr->mr_port,
|
|
l, MACH_PORT_TYPE_RECEIVE, name);
|
|
break;
|
|
|
|
default:
|
|
uprintf("mach_port_insert_right: unknown right %x\n",
|
|
req->req_poly.disposition);
|
|
break;
|
|
}
|
|
|
|
rep->rep_msgh.msgh_bits =
|
|
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
|
|
rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer);
|
|
rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port;
|
|
rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100;
|
|
rep->rep_trailer.msgh_trailer_size = 8;
|
|
|
|
*msglen = sizeof(*rep);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mach_port_type(args)
|
|
struct mach_trap_args *args;
|
|
{
|
|
mach_port_type_request_t *req = args->smsg;
|
|
mach_port_type_reply_t *rep = args->rmsg;
|
|
size_t *msglen = args->rsize;
|
|
struct lwp *l = args->l;
|
|
mach_port_t mn;
|
|
struct mach_right *mr;
|
|
|
|
mn = req->req_name;
|
|
if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == NULL)
|
|
return mach_msg_error(args, EPERM);
|
|
|
|
rep->rep_msgh.msgh_bits =
|
|
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
|
|
rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer);
|
|
rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port;
|
|
rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100;
|
|
rep->rep_retval = 0;
|
|
rep->rep_ptype = mr->mr_type;
|
|
rep->rep_trailer.msgh_trailer_size = 8;
|
|
|
|
*msglen = sizeof(*rep);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mach_port_set_attributes(args)
|
|
struct mach_trap_args *args;
|
|
{
|
|
mach_port_set_attributes_request_t *req = args->smsg;
|
|
mach_port_set_attributes_reply_t *rep = args->rmsg;
|
|
size_t *msglen = args->rsize;
|
|
int end_offset;
|
|
|
|
/* Sanity check req->req_count */
|
|
end_offset = req->req_count;
|
|
if (MACH_REQMSG_OVERFLOW(args, req->req_port_info[end_offset]))
|
|
return mach_msg_error(args, EINVAL);
|
|
|
|
switch(req->req_flavor) {
|
|
case MACH_PORT_LIMITS_INFO:
|
|
case MACH_PORT_RECEIVE_STATUS:
|
|
case MACH_PORT_DNREQUESTS_SIZE:
|
|
break;
|
|
default:
|
|
uprintf("mach_port_set_attributes: unknown flavor %d\n",
|
|
req->req_flavor);
|
|
break;
|
|
}
|
|
|
|
rep->rep_msgh.msgh_bits =
|
|
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
|
|
rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer);
|
|
rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port;
|
|
rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100;
|
|
rep->rep_trailer.msgh_trailer_size = 8;
|
|
|
|
*msglen = sizeof(*rep);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mach_port_get_attributes(args)
|
|
struct mach_trap_args *args;
|
|
{
|
|
mach_port_get_attributes_request_t *req = args->smsg;
|
|
mach_port_get_attributes_reply_t *rep = args->rmsg;
|
|
size_t *msglen = args->rsize;
|
|
struct lwp *l = args->l;
|
|
mach_port_t mn;
|
|
struct mach_right *mr;
|
|
|
|
/* Sanity check req_count */
|
|
if (req->req_count > 10)
|
|
return mach_msg_error(args, EINVAL);
|
|
|
|
mn = req->req_msgh.msgh_remote_port;
|
|
if ((mr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == NULL)
|
|
return mach_msg_error(args, EPERM);
|
|
|
|
switch (req->req_flavor) {
|
|
case MACH_PORT_LIMITS_INFO: {
|
|
struct mach_port_limits *mpl;
|
|
|
|
if (req->req_count < (sizeof(*mpl) / sizeof(rep->rep_info[0])))
|
|
return mach_msg_error(args, EINVAL);
|
|
|
|
mpl = (struct mach_port_limits *)&rep->rep_info[0];
|
|
mpl->mpl_qlimit = MACH_PORT_QLIMIT_DEFAULT; /* XXX fake limit */
|
|
|
|
rep->rep_count = sizeof(*mpl);
|
|
|
|
break;
|
|
}
|
|
|
|
case MACH_PORT_RECEIVE_STATUS: {
|
|
struct mach_port_status *mps;
|
|
struct mach_port *mp;
|
|
|
|
if (req->req_count < (sizeof(*mps) / sizeof(rep->rep_info[0])))
|
|
return mach_msg_error(args, EINVAL);
|
|
|
|
mps = (struct mach_port_status *)&rep->rep_info[0];
|
|
memset(mps, 0, sizeof(*mps));
|
|
|
|
if (mr->mr_sethead != NULL)
|
|
mps->mps_pset = mr->mr_sethead->mr_name;
|
|
mps->mps_seqno = 0; /* XXX */
|
|
mps->mps_qlimit = MACH_PORT_QLIMIT_DEFAULT; /* XXX fake limit */
|
|
if ((mp = mr->mr_port) != NULL) {
|
|
mps->mps_mscount = mp->mp_refcount; /* XXX */
|
|
mps->mps_msgcount = mp->mp_count;
|
|
} else {
|
|
mps->mps_mscount = 0;
|
|
mps->mps_msgcount = 0;
|
|
}
|
|
mps->mps_sorights = 0; /* XXX */
|
|
mps->mps_srights = 0; /* XXX */
|
|
if (mr->mr_notify_destroyed != NULL)
|
|
mps->mps_pdrequest = 1;
|
|
if (mr->mr_notify_no_senders != NULL)
|
|
mps->mps_nsrequest = 1;
|
|
mps->mps_flags = 0; /* XXX */
|
|
|
|
rep->rep_count = sizeof(*mps);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
printf("mach_port_get_attributes: unknown flavor %d\n",
|
|
req->req_flavor);
|
|
return mach_msg_error(args, EINVAL);
|
|
|
|
break;
|
|
};
|
|
|
|
*msglen = sizeof(*rep) - 10 + rep->rep_count;
|
|
|
|
rep->rep_msgh.msgh_bits =
|
|
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
|
|
rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer);
|
|
rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port;
|
|
rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100;
|
|
rep->rep_retval = 0;
|
|
rep->rep_info[rep->rep_count + 1] = 8;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* XXX insert a recv right into a port set without removing it from another */
|
|
int
|
|
mach_port_insert_member(args)
|
|
struct mach_trap_args *args;
|
|
{
|
|
mach_port_insert_member_request_t *req = args->smsg;
|
|
mach_port_insert_member_reply_t *rep = args->rmsg;
|
|
size_t *msglen = args->rsize;
|
|
|
|
uprintf("Unimplemented mach_port_insert_member\n");
|
|
|
|
rep->rep_msgh.msgh_bits =
|
|
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
|
|
rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer);
|
|
rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port;
|
|
rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100;
|
|
rep->rep_trailer.msgh_trailer_size = 8;
|
|
|
|
*msglen = sizeof(*rep);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mach_port_move_member(args)
|
|
struct mach_trap_args *args;
|
|
{
|
|
mach_port_move_member_request_t *req = args->smsg;
|
|
mach_port_move_member_reply_t *rep = args->rmsg;
|
|
size_t *msglen = args->rsize;
|
|
struct lwp *l = args->l;
|
|
struct mach_emuldata *med = l->l_proc->p_emuldata;
|
|
mach_port_t member = req->req_member;
|
|
mach_port_t after = req->req_after;
|
|
struct mach_right *mrr;
|
|
struct mach_right *mrs;
|
|
|
|
mrr = mach_right_check(member, l, MACH_PORT_TYPE_RECEIVE);
|
|
if (mrr == NULL)
|
|
return mach_msg_error(args, EPERM);
|
|
|
|
mrs = mach_right_check(after, l, MACH_PORT_TYPE_PORT_SET);
|
|
if (mrs == NULL)
|
|
return mach_msg_error(args, EPERM);
|
|
|
|
lockmgr(&med->med_rightlock, LK_EXCLUSIVE, NULL);
|
|
|
|
/* Remove it from an existing port set */
|
|
if (mrr->mr_sethead != mrr)
|
|
LIST_REMOVE(mrr, mr_setlist);
|
|
|
|
/* Insert it into the new port set */
|
|
LIST_INSERT_HEAD(&mrs->mr_set, mrr, mr_setlist);
|
|
mrr->mr_sethead = mrs;
|
|
|
|
lockmgr(&med->med_rightlock, LK_RELEASE, NULL);
|
|
|
|
rep->rep_msgh.msgh_bits =
|
|
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
|
|
rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer);
|
|
rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port;
|
|
rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100;
|
|
rep->rep_trailer.msgh_trailer_size = 8;
|
|
|
|
*msglen = sizeof(*rep);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mach_port_request_notification(args)
|
|
struct mach_trap_args *args;
|
|
{
|
|
mach_port_request_notification_request_t *req = args->smsg;
|
|
mach_port_request_notification_reply_t *rep = args->rmsg;
|
|
struct lwp *l = args->l;
|
|
size_t *msglen = args->rsize;
|
|
mach_port_t mn;
|
|
struct mach_right *nmr;
|
|
struct mach_right *tmr;
|
|
struct mach_right *oldnmr;
|
|
mach_port_t oldmn;
|
|
|
|
#ifdef DEBUG_MACH
|
|
printf("mach_port_request_notification, notify = %08x, target = %08x\n",
|
|
req->req_notify.name, mn = req->req_name);
|
|
#endif
|
|
mn = req->req_notify.name;
|
|
if ((nmr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == NULL)
|
|
return mach_msg_error(args, EINVAL);
|
|
|
|
mn = req->req_name;
|
|
if ((tmr = mach_right_check(mn, l, MACH_PORT_TYPE_ALL_RIGHTS)) == NULL)
|
|
return mach_msg_error(args, EINVAL);
|
|
|
|
#ifdef DEBUG_MACH
|
|
if (nmr->mr_port == NULL) {
|
|
printf("Notification right without a port\n");
|
|
printf("mr->mr_port = %p, mr = %08x\n", nmr->mr_port, nmr->mr_name);
|
|
return mach_msg_error(args, EINVAL);
|
|
}
|
|
#endif
|
|
|
|
|
|
oldnmr = NULL;
|
|
switch(req->req_msgid) {
|
|
case MACH_NOTIFY_DESTROYED_MSGID:
|
|
oldnmr = tmr->mr_notify_destroyed;
|
|
tmr->mr_notify_destroyed = mach_right_get(nmr->mr_port,
|
|
l, MACH_PORT_TYPE_SEND_ONCE, req->req_notify.name);
|
|
break;
|
|
|
|
case MACH_NOTIFY_NO_SENDERS_MSGID:
|
|
oldnmr = tmr->mr_notify_no_senders;
|
|
tmr->mr_notify_no_senders = mach_right_get(nmr->mr_port,
|
|
l, MACH_PORT_TYPE_SEND_ONCE, req->req_notify.name);
|
|
tmr->mr_notify_no_senders->mr_port->mp_datatype =
|
|
MACH_MP_NOTIFY_SYNC;
|
|
(int)tmr->mr_notify_no_senders->mr_port->mp_data =
|
|
req->req_count;
|
|
break;
|
|
|
|
case MACH_NOTIFY_DEAD_NAME_MSGID:
|
|
oldnmr = tmr->mr_notify_dead_name;
|
|
tmr->mr_notify_dead_name = mach_right_get(nmr->mr_port,
|
|
l, MACH_PORT_TYPE_SEND_ONCE, req->req_notify.name);
|
|
break;
|
|
|
|
case MACH_NOTIFY_SEND_ONCE_MSGID:
|
|
case MACH_NOTIFY_DELETED_MSGID:
|
|
default:
|
|
#ifdef DEBUG_MACH
|
|
printf("unsupported notify request %d\n", req->req_msgid);
|
|
return mach_msg_error(args, EINVAL);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
if (oldnmr != NULL) {
|
|
oldnmr->mr_refcount++;
|
|
oldmn = oldnmr->mr_name;
|
|
} else {
|
|
oldmn = (mach_port_t)MACH_PORT_NULL;
|
|
}
|
|
|
|
rep->rep_msgh.msgh_bits =
|
|
MACH_MSGH_REPLY_LOCAL_BITS(MACH_MSG_TYPE_MOVE_SEND_ONCE);
|
|
rep->rep_msgh.msgh_size = sizeof(*rep) - sizeof(rep->rep_trailer);
|
|
rep->rep_msgh.msgh_local_port = req->req_msgh.msgh_local_port;
|
|
rep->rep_msgh.msgh_id = req->req_msgh.msgh_id + 100;
|
|
rep->rep_body.msgh_descriptor_count = 1;
|
|
rep->rep_previous.name = oldmn;
|
|
rep->rep_previous.disposition = MACH_MSG_TYPE_MOVE_SEND_ONCE;
|
|
rep->rep_trailer.msgh_trailer_size = 8;
|
|
|
|
*msglen = sizeof(*rep);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
mach_port_init(void)
|
|
{
|
|
pool_init(&mach_port_pool, sizeof (struct mach_port),
|
|
0, 0, 128, "mach_port_pool", NULL);
|
|
pool_init(&mach_right_pool, sizeof (struct mach_right),
|
|
0, 0, 128, "mach_right_pool", NULL);
|
|
|
|
mach_bootstrap_port = mach_port_get();
|
|
mach_clock_port = mach_port_get();
|
|
mach_io_master_port = mach_port_get();
|
|
|
|
mach_bootstrap_port->mp_flags |= MACH_MP_INKERNEL;
|
|
mach_clock_port->mp_flags |= MACH_MP_INKERNEL;
|
|
mach_io_master_port->mp_flags |= MACH_MP_INKERNEL;
|
|
|
|
mach_saved_bootstrap_port = mach_bootstrap_port;
|
|
|
|
return;
|
|
}
|
|
|
|
struct mach_port *
|
|
mach_port_get(void)
|
|
{
|
|
struct mach_port *mp;
|
|
|
|
mp = (struct mach_port *)pool_get(&mach_port_pool, M_WAITOK);
|
|
bzero(mp, sizeof(*mp));
|
|
mp->mp_recv = NULL;
|
|
mp->mp_count = 0;
|
|
mp->mp_flags = 0;
|
|
mp->mp_datatype = MACH_MP_NONE;
|
|
mp->mp_data = NULL;
|
|
TAILQ_INIT(&mp->mp_msglist);
|
|
lockinit(&mp->mp_msglock, PZERO|PCATCH, "mach_port", 0, 0);
|
|
|
|
return mp;
|
|
}
|
|
|
|
void
|
|
mach_port_put(mp)
|
|
struct mach_port *mp;
|
|
{
|
|
struct mach_message *mm;
|
|
|
|
if (mp->mp_refcount > 0) {
|
|
uprintf("mach_port_put: trying to free a referenced port\n");
|
|
return;
|
|
}
|
|
|
|
lockmgr(&mp->mp_msglock, LK_EXCLUSIVE, NULL);
|
|
while ((mm = TAILQ_FIRST(&mp->mp_msglist)) != NULL)
|
|
mach_message_put_exclocked(mm);
|
|
lockmgr(&mp->mp_msglock, LK_RELEASE, NULL);
|
|
lockmgr(&mp->mp_msglock, LK_DRAIN, NULL);
|
|
|
|
if (mp->mp_flags & MACH_MP_DATA_ALLOCATED)
|
|
free(mp->mp_data, M_EMULDATA);
|
|
|
|
pool_put(&mach_port_pool, mp);
|
|
|
|
return;
|
|
}
|
|
|
|
struct mach_right *
|
|
mach_right_get(mp, l, type, hint)
|
|
struct mach_port *mp;
|
|
struct lwp *l;
|
|
int type;
|
|
mach_port_t hint;
|
|
{
|
|
struct mach_right *mr;
|
|
struct mach_emuldata *med;
|
|
int rights;
|
|
|
|
#ifdef DEBUG_MACH
|
|
if (type == 0)
|
|
uprintf("mach_right_get: right = 0\n");
|
|
#endif
|
|
med = (struct mach_emuldata *)l->l_proc->p_emuldata;
|
|
|
|
/* Send and receive right must return an existing right */
|
|
rights = (MACH_PORT_TYPE_SEND | MACH_PORT_TYPE_RECEIVE);
|
|
if (type & rights) {
|
|
lockmgr(&med->med_rightlock, LK_SHARED, NULL);
|
|
LIST_FOREACH(mr, &med->med_right, mr_list) {
|
|
if ((mr->mr_port == mp) && (mr->mr_type & rights))
|
|
break;
|
|
}
|
|
lockmgr(&med->med_rightlock, LK_RELEASE, NULL);
|
|
|
|
if (mr != NULL) {
|
|
mr->mr_type |= type;
|
|
if (type & MACH_PORT_TYPE_SEND)
|
|
mr->mr_refcount++;
|
|
goto rcvck;
|
|
}
|
|
}
|
|
|
|
mr = pool_get(&mach_right_pool, M_WAITOK);
|
|
|
|
mr->mr_port = mp;
|
|
mr->mr_lwp = l;
|
|
mr->mr_type = type;
|
|
mr->mr_sethead = mr;
|
|
mr->mr_refcount = 1;
|
|
mr->mr_notify_destroyed = NULL;
|
|
mr->mr_notify_dead_name = NULL;
|
|
mr->mr_notify_no_senders = NULL;
|
|
|
|
LIST_INIT(&mr->mr_set);
|
|
|
|
if (mp != NULL)
|
|
mp->mp_refcount++;
|
|
|
|
/* Insert the right in the right lists */
|
|
if (type & MACH_PORT_TYPE_ALL_RIGHTS) {
|
|
lockmgr(&med->med_rightlock, LK_EXCLUSIVE, NULL);
|
|
mr->mr_name = mach_right_newname(l, hint);
|
|
#ifdef DEBUG_MACH_RIGHT
|
|
printf("mach_right_get: insert right %x(%x)\n",
|
|
mr->mr_name, mr->mr_type);
|
|
#endif
|
|
LIST_INSERT_HEAD(&med->med_right, mr, mr_list);
|
|
lockmgr(&med->med_rightlock, LK_RELEASE, NULL);
|
|
}
|
|
|
|
rcvck:
|
|
if (type & MACH_PORT_TYPE_RECEIVE) {
|
|
/*
|
|
* Destroy the former receive right on this port, and
|
|
* register the new right.
|
|
*/
|
|
if (mr->mr_port->mp_recv != NULL)
|
|
mach_right_put(mr->mr_port->mp_recv,
|
|
MACH_PORT_TYPE_RECEIVE);
|
|
mr->mr_port->mp_recv = mr;
|
|
}
|
|
return mr;
|
|
}
|
|
|
|
void
|
|
mach_right_put(mr, right)
|
|
struct mach_right *mr;
|
|
int right;
|
|
{
|
|
struct mach_emuldata *med = mr->mr_lwp->l_proc->p_emuldata;
|
|
|
|
lockmgr(&med->med_rightlock, LK_EXCLUSIVE, NULL);
|
|
mach_right_put_exclocked(mr, right);
|
|
lockmgr(&med->med_rightlock, LK_RELEASE, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
mach_right_put_shlocked(mr, right)
|
|
struct mach_right *mr;
|
|
int right;
|
|
{
|
|
struct mach_emuldata *med = mr->mr_lwp->l_proc->p_emuldata;
|
|
|
|
lockmgr(&med->med_rightlock, LK_UPGRADE, NULL);
|
|
mach_right_put_exclocked(mr, right);
|
|
lockmgr(&med->med_rightlock, LK_DOWNGRADE, NULL);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
mach_right_put_exclocked(mr, right)
|
|
struct mach_right *mr;
|
|
int right;
|
|
{
|
|
struct mach_right *cmr;
|
|
struct mach_emuldata *med;
|
|
int lright;
|
|
int kill_right;
|
|
|
|
med = mr->mr_lwp->l_proc->p_emuldata;
|
|
|
|
#ifdef DEBUG_MACH_RIGHT
|
|
printf("mach_right_put: mr = %p\n", mr);
|
|
printf("right %x(%x) refcount %d, deallocate %x\n",
|
|
mr->mr_name, mr->mr_type, mr->mr_refcount, right);
|
|
if ((mr->mr_type & right) == 0)
|
|
printf("mach_right_put: dropping nonexistant right %x on %x\n",
|
|
right, mr->mr_name);
|
|
LIST_FOREACH(cmr, &med->med_right, mr_list)
|
|
if (cmr == mr)
|
|
break;
|
|
if (cmr == NULL) {
|
|
printf("mach_right_put: dropping already dropped right %x\n",
|
|
mr->mr_name);
|
|
return;
|
|
}
|
|
#endif
|
|
kill_right = 0;
|
|
|
|
/* When receive right is deallocated, the port should die */
|
|
lright = (right & MACH_PORT_TYPE_RECEIVE);
|
|
#ifdef DEBUG_MACH_RIGHT
|
|
printf("mr->mr_type = %x, lright = %x, right = %x, refcount = %d\n",
|
|
mr->mr_type, lright, right, mr->mr_refcount);
|
|
#endif
|
|
if (mr->mr_type & lright) {
|
|
if (mr->mr_refcount <= 0) {
|
|
mr->mr_type &= ~MACH_PORT_TYPE_RECEIVE;
|
|
kill_right = 1;
|
|
} else {
|
|
mr->mr_type &= ~MACH_PORT_TYPE_RECEIVE;
|
|
mr->mr_type |= MACH_PORT_TYPE_DEAD_NAME;
|
|
mach_notify_port_dead_name(mr->mr_lwp, mr);
|
|
}
|
|
if (mr->mr_port != NULL) {
|
|
mr->mr_port->mp_refcount--;
|
|
if (mr->mr_port->mp_refcount <= 0)
|
|
mach_port_put(mr->mr_port);
|
|
mr->mr_port = NULL;
|
|
}
|
|
}
|
|
|
|
/* send, send_once and dead_name */
|
|
lright = (right & MACH_PORT_TYPE_REF_RIGHTS);
|
|
if (mr->mr_type & lright) {
|
|
mr->mr_refcount--;
|
|
|
|
mach_notify_port_no_senders(mr->mr_lwp, mr);
|
|
|
|
if (mr->mr_refcount <= 0) {
|
|
mr->mr_type &= ~MACH_PORT_TYPE_REF_RIGHTS;
|
|
if ((mr->mr_type & MACH_PORT_TYPE_RECEIVE) == 0)
|
|
kill_right = 1;
|
|
}
|
|
}
|
|
|
|
lright = (right & MACH_PORT_TYPE_PORT_SET);
|
|
if ((mr->mr_type & lright) || (kill_right == 1)) {
|
|
while ((cmr = LIST_FIRST(&mr->mr_set)) != NULL) {
|
|
LIST_REMOVE(cmr, mr_setlist);
|
|
cmr->mr_sethead = cmr;
|
|
}
|
|
mr->mr_type &= ~MACH_PORT_TYPE_PORT_SET;
|
|
if ((mr->mr_type & MACH_PORT_TYPE_RECEIVE) == 0)
|
|
kill_right = 1;
|
|
}
|
|
|
|
/* Should we kill it? */
|
|
if (kill_right == 1) {
|
|
#ifdef DEBUG_MACH_RIGHT
|
|
printf("mach_right_put: kill name %x\n", mr->mr_name);
|
|
#endif
|
|
/* If the right is used for an IO notification, remove it */
|
|
mach_iokit_cleanup_notify(mr);
|
|
|
|
mach_notify_port_destroyed(mr->mr_lwp, mr);
|
|
LIST_REMOVE(mr, mr_list);
|
|
pool_put(&mach_right_pool, mr);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Check that a process do have a given right
|
|
*/
|
|
struct mach_right *
|
|
mach_right_check(mn, l, type)
|
|
mach_port_t mn;
|
|
struct lwp *l;
|
|
int type;
|
|
{
|
|
struct mach_right *cmr;
|
|
struct mach_emuldata *med;
|
|
|
|
if ((mn == 0) || (mn == -1) || (l == NULL))
|
|
return NULL;
|
|
|
|
med = (struct mach_emuldata *)l->l_proc->p_emuldata;
|
|
|
|
lockmgr(&med->med_rightlock, LK_SHARED, NULL);
|
|
|
|
#ifdef DEBUG_MACH_RIGHT
|
|
printf("mach_right_check: type = %x, mn = %x\n", type, mn);
|
|
#endif
|
|
LIST_FOREACH(cmr, &med->med_right, mr_list) {
|
|
#ifdef DEBUG_MACH_RIGHT
|
|
printf("cmr = %p, cmr->mr_name = %x, cmr->mr_type = %x\n",
|
|
cmr, cmr->mr_name, cmr->mr_type);
|
|
#endif
|
|
if (cmr->mr_name != mn)
|
|
continue;
|
|
if (type & cmr->mr_type)
|
|
break;
|
|
}
|
|
|
|
lockmgr(&med->med_rightlock, LK_RELEASE, NULL);
|
|
|
|
return cmr;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find an usnused port name in a given lwp.
|
|
* Right lists should be locked.
|
|
*/
|
|
mach_port_t
|
|
mach_right_newname(l, hint)
|
|
struct lwp *l;
|
|
mach_port_t hint;
|
|
{
|
|
struct mach_emuldata *med;
|
|
struct mach_right *mr;
|
|
mach_port_t newname = -1;
|
|
|
|
med = l->l_proc->p_emuldata;
|
|
|
|
if (hint == 0)
|
|
hint = med->med_nextright;
|
|
|
|
while (newname == -1) {
|
|
LIST_FOREACH(mr, &med->med_right, mr_list)
|
|
if (mr->mr_name == hint)
|
|
break;
|
|
if (mr == NULL)
|
|
newname = hint;
|
|
hint++;
|
|
}
|
|
|
|
med->med_nextright = hint;
|
|
|
|
return newname;
|
|
}
|
|
|
|
#ifdef DEBUG_MACH
|
|
void
|
|
mach_debug_port(void)
|
|
{
|
|
struct lwp *l;
|
|
struct mach_emuldata *med;
|
|
struct mach_right *mr;
|
|
struct mach_right *mrs;
|
|
struct proc *p = l->l_proc;
|
|
|
|
LIST_FOREACH(l, &alllwp, l_list) {
|
|
if ((p->p_emul != &emul_mach) &&
|
|
#ifdef COMPAT_DARWIN
|
|
(p->p_emul != &emul_darwin) &&
|
|
#endif
|
|
1)
|
|
continue;
|
|
|
|
med = p->p_emuldata;
|
|
LIST_FOREACH(mr, &med->med_right, mr_list) {
|
|
if ((mr->mr_type & MACH_PORT_TYPE_PORT_SET) == 0) {
|
|
printf("pid %d: %p(%x)=>%p",
|
|
p->p_pid, mr, mr->mr_type, mr->mr_port);
|
|
if (mr->mr_port != NULL)
|
|
printf("[%p]\n",
|
|
mr->mr_port->mp_recv->mr_sethead);
|
|
else
|
|
printf("[NULL]\n");
|
|
|
|
continue;
|
|
}
|
|
|
|
/* Port set... */
|
|
printf("pid %d: set %p(%x) ",
|
|
p->p_pid, mr, mr->mr_type);
|
|
LIST_FOREACH(mrs, &mr->mr_set, mr_setlist) {
|
|
printf("%p(%x)=>%p",
|
|
mrs, mrs->mr_type, mrs->mr_port);
|
|
if (mrs->mr_port != NULL)
|
|
printf("[%p]",
|
|
mrs->mr_port->mp_recv->mr_sethead);
|
|
else
|
|
printf("[NULL]");
|
|
|
|
printf(" ");
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
#endif /* DEBUG_MACH */
|