X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/3a60a9f5b85abb8c2cf24e1926c5c7b3f608a5e2..c910b4d9d2451126ae3917b931cd4390c11e1d52:/bsd/kern/uipc_usrreq.c diff --git a/bsd/kern/uipc_usrreq.c b/bsd/kern/uipc_usrreq.c index 78487fdcf..9b568d24c 100644 --- a/bsd/kern/uipc_usrreq.c +++ b/bsd/kern/uipc_usrreq.c @@ -1,23 +1,29 @@ /* - * Copyright (c) 2000-2004 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@ */ /* * Copyright (c) 1982, 1986, 1989, 1991, 1993 @@ -53,6 +59,12 @@ * * From: @(#)uipc_usrreq.c 8.3 (Berkeley) 1/4/94 */ +/* + * NOTICE: This file was modified by SPARTA, Inc. in 2005 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. + */ #include #include @@ -80,17 +92,25 @@ #include #include -#define f_msgcount f_fglob->fg_msgcount -#define f_cred f_fglob->fg_cred -#define f_ops f_fglob->fg_ops -#define f_offset f_fglob->fg_offset -#define f_data f_fglob->fg_data +#if CONFIG_MACF_SOCKET +#include +#endif /* MAC_SOCKET */ + +#define f_msgcount f_fglob->fg_msgcount +#define f_cred f_fglob->fg_cred +#define f_ops f_fglob->fg_ops +#define f_offset f_fglob->fg_offset +#define f_data f_fglob->fg_data struct zone *unp_zone; static unp_gen_t unp_gencnt; static u_int unp_count; -static lck_mtx_t *unp_mutex; -extern lck_mtx_t * uipc_lock; +static lck_attr_t *unp_mtx_attr; +static lck_grp_t *unp_mtx_grp; +static lck_grp_attr_t *unp_mtx_grp_attr; +static lck_rw_t *unp_list_mtx; + +extern lck_mtx_t *uipc_lock; static struct unp_head unp_shead, unp_dhead; /* @@ -102,24 +122,26 @@ static struct unp_head unp_shead, unp_dhead; * need a proper out-of-band * lock pushdown */ -static struct sockaddr sun_noname = { sizeof(sun_noname), AF_LOCAL, { 0 } }; +static struct sockaddr sun_noname = { sizeof (sun_noname), AF_LOCAL, { 0 } }; static ino_t unp_ino; /* prototype for fake inode numbers */ -static int unp_attach(struct socket *); -static void unp_detach(struct unpcb *); -static int unp_bind(struct unpcb *,struct sockaddr *, struct proc *); -static int unp_connect(struct socket *,struct sockaddr *, struct proc *); -static void unp_disconnect(struct unpcb *); -static void unp_shutdown(struct unpcb *); -static void unp_drop(struct unpcb *, int); -static void unp_gc(void); -static void unp_scan(struct mbuf *, void (*)(struct fileglob *)); -static void unp_mark(struct fileglob *); -static void unp_discard(struct fileglob *); -static void unp_discard_fdlocked(struct fileglob *, struct proc *); -static int unp_internalize(struct mbuf *, struct proc *); -static int unp_listen(struct unpcb *, struct proc *); - +static int unp_attach(struct socket *); +static void unp_detach(struct unpcb *); +static int unp_bind(struct unpcb *, struct sockaddr *, proc_t); +static int unp_connect(struct socket *, struct sockaddr *, proc_t); +static void unp_disconnect(struct unpcb *); +static void unp_shutdown(struct unpcb *); +static void unp_drop(struct unpcb *, int); +static void unp_gc(void); +static void unp_scan(struct mbuf *, void (*)(struct fileglob *)); +static void unp_mark(struct fileglob *); +static void unp_discard(struct fileglob *); +static void unp_discard_fdlocked(struct fileglob *, proc_t); +static int unp_internalize(struct mbuf *, proc_t); +static int unp_listen(struct unpcb *, proc_t); + +/* TODO: this should be in header file */ +extern int fdgetf_noref(proc_t, int, struct fileproc **); static int uipc_abort(struct socket *so) @@ -127,11 +149,11 @@ uipc_abort(struct socket *so) struct unpcb *unp = sotounpcb(so); if (unp == 0) - return EINVAL; + return (EINVAL); unp_drop(unp, ECONNABORTED); unp_detach(unp); sofree(so); - return 0; + return (0); } static int @@ -140,7 +162,7 @@ uipc_accept(struct socket *so, struct sockaddr **nam) struct unpcb *unp = sotounpcb(so); if (unp == 0) - return EINVAL; + return (EINVAL); /* * Pass back name of connected socket, @@ -148,54 +170,70 @@ uipc_accept(struct socket *so, struct sockaddr **nam) * (our peer may have closed already!). */ if (unp->unp_conn && unp->unp_conn->unp_addr) { - *nam = dup_sockaddr((struct sockaddr *)unp->unp_conn->unp_addr, - 1); + *nam = dup_sockaddr((struct sockaddr *) + unp->unp_conn->unp_addr, 1); } else { *nam = dup_sockaddr((struct sockaddr *)&sun_noname, 1); } - return 0; + return (0); } +/* + * Returns: 0 Success + * EISCONN + * unp_attach: + */ static int -uipc_attach(struct socket *so, __unused int proto, __unused struct proc *p) +uipc_attach(struct socket *so, __unused int proto, __unused proc_t p) { struct unpcb *unp = sotounpcb(so); if (unp != 0) - return EISCONN; - return unp_attach(so); + return (EISCONN); + return (unp_attach(so)); } static int -uipc_bind(struct socket *so, struct sockaddr *nam, struct proc *p) +uipc_bind(struct socket *so, struct sockaddr *nam, proc_t p) { struct unpcb *unp = sotounpcb(so); if (unp == 0) - return EINVAL; + return (EINVAL); - return unp_bind(unp, nam, p); + return (unp_bind(unp, nam, p)); } +/* + * Returns: 0 Success + * EINVAL + * unp_connect:??? [See elsewhere in this file] + */ static int -uipc_connect(struct socket *so, struct sockaddr *nam, struct proc *p) +uipc_connect(struct socket *so, struct sockaddr *nam, proc_t p) { struct unpcb *unp = sotounpcb(so); if (unp == 0) - return EINVAL; - return unp_connect(so, nam, p); + return (EINVAL); + return (unp_connect(so, nam, p)); } +/* + * Returns: 0 Success + * EINVAL + * unp_connect2:EPROTOTYPE Protocol wrong type for socket + * unp_connect2:EINVAL Invalid argument + */ static int uipc_connect2(struct socket *so1, struct socket *so2) { struct unpcb *unp = sotounpcb(so1); if (unp == 0) - return EINVAL; + return (EINVAL); - return unp_connect2(so1, so2); + return (unp_connect2(so1, so2)); } /* control is EOPNOTSUPP */ @@ -206,10 +244,10 @@ uipc_detach(struct socket *so) struct unpcb *unp = sotounpcb(so); if (unp == 0) - return EINVAL; + return (EINVAL); unp_detach(unp); - return 0; + return (0); } static int @@ -218,19 +256,23 @@ uipc_disconnect(struct socket *so) struct unpcb *unp = sotounpcb(so); if (unp == 0) - return EINVAL; + return (EINVAL); unp_disconnect(unp); - return 0; + return (0); } +/* + * Returns: 0 Success + * EINVAL + */ static int -uipc_listen(struct socket *so, __unused struct proc *p) +uipc_listen(struct socket *so, __unused proc_t p) { struct unpcb *unp = sotounpcb(so); if (unp == 0 || unp->unp_vnode == 0) - return EINVAL; - return unp_listen(unp, p); + return (EINVAL); + return (unp_listen(unp, p)); } static int @@ -238,12 +280,15 @@ uipc_peeraddr(struct socket *so, struct sockaddr **nam) { struct unpcb *unp = sotounpcb(so); - if (unp == 0) - return EINVAL; - if (unp->unp_conn && unp->unp_conn->unp_addr) - *nam = dup_sockaddr((struct sockaddr *)unp->unp_conn->unp_addr, - 1); - return 0; + if (unp == NULL) + return (EINVAL); + if (unp->unp_conn != NULL && unp->unp_conn->unp_addr != NULL) { + *nam = dup_sockaddr((struct sockaddr *) + unp->unp_conn->unp_addr, 1); + } else { + *nam = dup_sockaddr((struct sockaddr *)&sun_noname, 1); + } + return (0); } static int @@ -253,7 +298,7 @@ uipc_rcvd(struct socket *so, __unused int flags) struct socket *so2; if (unp == 0) - return EINVAL; + return (EINVAL); switch (so->so_type) { case SOCK_DGRAM: panic("uipc_rcvd DGRAM?"); @@ -261,7 +306,7 @@ uipc_rcvd(struct socket *so, __unused int flags) case SOCK_STREAM: #define rcv (&so->so_rcv) -#define snd (&so2->so_snd) +#define snd (&so2->so_snd) if (unp->unp_conn == 0) break; so2 = unp->unp_conn->unp_socket; @@ -281,14 +326,33 @@ uipc_rcvd(struct socket *so, __unused int flags) default: panic("uipc_rcvd unknown socktype"); } - return 0; + return (0); } /* pru_rcvoob is EOPNOTSUPP */ +/* + * Returns: 0 Success + * EINVAL + * EOPNOTSUPP + * EPIPE + * ENOTCONN + * EISCONN + * unp_internalize:EINVAL + * unp_internalize:EBADF + * unp_connect:EAFNOSUPPORT Address family not supported + * unp_connect:EINVAL Invalid argument + * unp_connect:ENOTSOCK Not a socket + * unp_connect:ECONNREFUSED Connection refused + * unp_connect:EISCONN Socket is connected + * unp_connect:EPROTOTYPE Protocol wrong type for socket + * unp_connect:??? + * sbappendaddr:ENOBUFS [5th argument, contents modified] + * sbappendaddr:??? [whatever a filter author chooses] + */ static int uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, - struct mbuf *control, struct proc *p) + struct mbuf *control, proc_t p) { int error = 0; struct unpcb *unp = sotounpcb(so); @@ -303,11 +367,17 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, goto release; } - if (control && (error = unp_internalize(control, p))) - goto release; + if (control) { + /* release global lock to avoid deadlock (4436174) */ + socket_unlock(so, 0); + error = unp_internalize(control, p); + socket_lock(so, 0); + if (error) + goto release; + } switch (so->so_type) { - case SOCK_DGRAM: + case SOCK_DGRAM: { struct sockaddr *from; @@ -330,11 +400,22 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, from = (struct sockaddr *)unp->unp_addr; else from = &sun_noname; + /* + * sbappendaddr() will fail when the receiver runs out of + * space; in contrast to SOCK_STREAM, we will lose messages + * for the SOCK_DGRAM case when the receiver's queue overflows. + * SB_UNIX on the socket buffer implies that the callee will + * not free the control message, if any, because we would need + * to call unp_dispose() on it. + */ if (sbappendaddr(&so2->so_rcv, from, m, control, &error)) { + control = NULL; sorwakeup(so2); + } else if (control != NULL && error == 0) { + /* A socket filter took control; don't touch it */ + control = NULL; } - m = 0; - control = 0; + m = NULL; if (nam) unp_disconnect(unp); break; @@ -368,23 +449,29 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, panic("uipc_send connected but no connection?"); so2 = unp->unp_conn->unp_socket; /* - * Send to paired receive port, and then reduce - * send buffer hiwater marks to maintain backpressure. - * Wake up readers. + * Send to paired receive port, and then reduce send buffer + * hiwater marks to maintain backpressure. Wake up readers. + * SB_UNIX flag will allow new record to be appended to the + * receiver's queue even when it is already full. It is + * possible, however, that append might fail. In that case, + * we will need to call unp_dispose() on the control message; + * the callee will not free it since SB_UNIX is set. */ - if ((control && sbappendcontrol(rcv, m, control, NULL)) || - sbappend(rcv, m)) { - didreceive = 1; - } - snd->sb_mbmax -= - rcv->sb_mbcnt - unp->unp_conn->unp_mbcnt; + didreceive = control ? + sbappendcontrol(rcv, m, control, &error) : sbappend(rcv, m); + + snd->sb_mbmax -= rcv->sb_mbcnt - unp->unp_conn->unp_mbcnt; unp->unp_conn->unp_mbcnt = rcv->sb_mbcnt; snd->sb_hiwat -= rcv->sb_cc - unp->unp_conn->unp_cc; unp->unp_conn->unp_cc = rcv->sb_cc; - if (didreceive) + if (didreceive) { + control = NULL; sorwakeup(so2); - m = 0; - control = 0; + } else if (control != NULL && error == 0) { + /* A socket filter took control; don't touch it */ + control = NULL; + } + m = NULL; #undef snd #undef rcv } @@ -403,59 +490,95 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, unp_shutdown(unp); } - if (control && error != 0) + if (control && error != 0) { + socket_unlock(so, 0); unp_dispose(control); + socket_lock(so, 0); + } release: if (control) m_freem(control); if (m) m_freem(m); - return error; + return (error); } static int -uipc_sense(struct socket *so, struct stat *sb) +uipc_sense(struct socket *so, void *ub, int isstat64) { struct unpcb *unp = sotounpcb(so); struct socket *so2; + blksize_t blksize; if (unp == 0) - return EINVAL; - sb->st_blksize = so->so_snd.sb_hiwat; + return (EINVAL); + + blksize = so->so_snd.sb_hiwat; if (so->so_type == SOCK_STREAM && unp->unp_conn != 0) { so2 = unp->unp_conn->unp_socket; - sb->st_blksize += so2->so_rcv.sb_cc; + blksize += so2->so_rcv.sb_cc; } - sb->st_dev = NODEV; if (unp->unp_ino == 0) unp->unp_ino = unp_ino++; - sb->st_ino = unp->unp_ino; + + if (isstat64 != 0) { + struct stat64 *sb64; + + sb64 = (struct stat64 *)ub; + sb64->st_blksize = blksize; + sb64->st_dev = NODEV; + sb64->st_ino = (ino64_t)unp->unp_ino; + } else { + struct stat *sb; + + sb = (struct stat *)ub; + sb->st_blksize = blksize; + sb->st_dev = NODEV; + sb->st_ino = (ino_t)unp->unp_ino; + } + return (0); } +/* + * Returns: 0 Success + * EINVAL + * + * Notes: This is not strictly correct, as unp_shutdown() also calls + * socantrcvmore(). These should maybe both be conditionalized + * on the 'how' argument in soshutdown() as called from the + * shutdown() system call. + */ static int uipc_shutdown(struct socket *so) { struct unpcb *unp = sotounpcb(so); if (unp == 0) - return EINVAL; + return (EINVAL); socantsendmore(so); unp_shutdown(unp); - return 0; + return (0); } +/* + * Returns: 0 Success + * EINVAL Invalid argument + */ static int uipc_sockaddr(struct socket *so, struct sockaddr **nam) { struct unpcb *unp = sotounpcb(so); - if (unp == 0) - return EINVAL; - if (unp->unp_addr) + if (unp == NULL) + return (EINVAL); + if (unp->unp_addr != NULL) { *nam = dup_sockaddr((struct sockaddr *)unp->unp_addr, 1); - return 0; + } else { + *nam = dup_sockaddr((struct sockaddr *)&sun_noname, 1); + } + return (0); } struct pr_usrreqs uipc_usrreqs = { @@ -467,9 +590,7 @@ struct pr_usrreqs uipc_usrreqs = { }; int -uipc_ctloutput( - struct socket *so, - struct sockopt *sopt) +uipc_ctloutput(struct socket *so, struct sockopt *sopt) { struct unpcb *unp = sotounpcb(so); int error; @@ -478,10 +599,10 @@ uipc_ctloutput( case SOPT_GET: switch (sopt->sopt_name) { case LOCAL_PEERCRED: - if (unp->unp_flags & UNP_HAVEPC) + if (unp->unp_flags & UNP_HAVEPC) { error = sooptcopyout(sopt, &unp->unp_peercred, - sizeof(unp->unp_peercred)); - else { + sizeof (unp->unp_peercred)); + } else { if (so->so_type == SOCK_STREAM) error = ENOTCONN; else @@ -500,7 +621,7 @@ uipc_ctloutput( } return (error); } - + /* * Both send and receive buffers are allocated PIPSIZ bytes of buffering * for stream sockets, although the total for sender and receiver is @@ -518,20 +639,26 @@ static u_long unpdg_sendspace = 2*1024; /* really max datagram size */ static u_long unpdg_recvspace = 4*1024; static int unp_rights; /* file descriptors in flight */ +static int unp_disposed; /* discarded file descriptors */ SYSCTL_DECL(_net_local_stream); -SYSCTL_INT(_net_local_stream, OID_AUTO, sendspace, CTLFLAG_RW, - &unpst_sendspace, 0, ""); +SYSCTL_INT(_net_local_stream, OID_AUTO, sendspace, CTLFLAG_RW, + &unpst_sendspace, 0, ""); SYSCTL_INT(_net_local_stream, OID_AUTO, recvspace, CTLFLAG_RW, - &unpst_recvspace, 0, ""); + &unpst_recvspace, 0, ""); SYSCTL_DECL(_net_local_dgram); SYSCTL_INT(_net_local_dgram, OID_AUTO, maxdgram, CTLFLAG_RW, - &unpdg_sendspace, 0, ""); + &unpdg_sendspace, 0, ""); SYSCTL_INT(_net_local_dgram, OID_AUTO, recvspace, CTLFLAG_RW, - &unpdg_recvspace, 0, ""); + &unpdg_recvspace, 0, ""); SYSCTL_DECL(_net_local); SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0, ""); +/* + * Returns: 0 Success + * ENOBUFS + * soreserve:ENOBUFS + */ static int unp_attach(struct socket *so) { @@ -555,33 +682,54 @@ unp_attach(struct socket *so) if (error) return (error); } - unp = (struct unpcb*)zalloc(unp_zone); + unp = (struct unpcb *)zalloc(unp_zone); if (unp == NULL) return (ENOBUFS); - bzero(unp, sizeof *unp); - lck_mtx_lock(unp_mutex); + bzero(unp, sizeof (*unp)); + lck_rw_lock_exclusive(unp_list_mtx); LIST_INIT(&unp->unp_refs); unp->unp_socket = so; unp->unp_gencnt = ++unp_gencnt; unp_count++; - LIST_INSERT_HEAD(so->so_type == SOCK_DGRAM ? &unp_dhead - : &unp_shead, unp, unp_link); + LIST_INSERT_HEAD(so->so_type == SOCK_DGRAM ? + &unp_dhead : &unp_shead, unp, unp_link); so->so_pcb = (caddr_t)unp; - lck_mtx_unlock(unp_mutex); + /* + * Mark AF_UNIX socket buffers accordingly so that: + * + * a. In the SOCK_STREAM case, socket buffer append won't fail due to + * the lack of space; this essentially loosens the sbspace() check, + * since there is disconnect between sosend() and uipc_send() with + * respect to flow control that might result in our dropping the + * data in uipc_send(). By setting this, we allow for slightly + * more records to be appended to the receiving socket to avoid + * losing data (which we can't afford in the SOCK_STREAM case). + * Flow control still takes place since we adjust the sender's + * hiwat during each send. This doesn't affect the SOCK_DGRAM + * case and append would still fail when the queue overflows. + * + * b. In the presence of control messages containing internalized + * file descriptors, the append routines will not free them since + * we'd need to undo the work first via unp_dispose(). + */ + so->so_rcv.sb_flags |= SB_UNIX; + so->so_snd.sb_flags |= SB_UNIX; + lck_rw_done(unp_list_mtx); return (0); } static void unp_detach(struct unpcb *unp) { - lck_mtx_assert(unp_mutex, LCK_MTX_ASSERT_OWNED); + lck_rw_lock_exclusive(unp_list_mtx); LIST_REMOVE(unp, unp_link); unp->unp_gencnt = ++unp_gencnt; + lck_rw_done(unp_list_mtx); --unp_count; if (unp->unp_vnode) { struct vnode *tvp = unp->unp_vnode; - unp->unp_vnode->v_socket = 0; - unp->unp_vnode = 0; + unp->unp_vnode->v_socket = NULL; + unp->unp_vnode = NULL; vnode_rele(tvp); /* drop the usecount */ } if (unp->unp_conn) @@ -589,8 +737,9 @@ unp_detach(struct unpcb *unp) while (unp->unp_refs.lh_first) unp_drop(unp->unp_refs.lh_first, ECONNRESET); soisdisconnected(unp->unp_socket); - unp->unp_socket->so_flags |= SOF_PCBCLEARING; /* makes sure we're getting dealloced */ - unp->unp_socket->so_pcb = 0; + /* makes sure we're getting dealloced */ + unp->unp_socket->so_flags |= SOF_PCBCLEARING; + unp->unp_socket->so_pcb = NULL; if (unp_rights) { /* * Normally the receive buffer is flushed later, @@ -607,33 +756,45 @@ unp_detach(struct unpcb *unp) zfree(unp_zone, unp); } +/* + * Returns: 0 Success + * EAFNOSUPPORT + * EINVAL + * EADDRINUSE + * namei:??? [anything namei can return] + * vnode_authorize:??? [anything vnode_authorize can return] + * + * Notes: p at this point is the current process, as this function is + * only called by sobind(). + */ static int unp_bind( struct unpcb *unp, struct sockaddr *nam, - struct proc *p) + proc_t p) { struct sockaddr_un *soun = (struct sockaddr_un *)nam; struct vnode *vp, *dvp; struct vnode_attr va; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); int error, namelen; struct nameidata nd; char buf[SOCK_MAXADDRLEN]; - context.vc_proc = p; - context.vc_ucred = p->p_ucred; /* XXX kauth_cred_get() ??? proxy */ + if (nam->sa_family != 0 && nam->sa_family != AF_UNIX) { + return (EAFNOSUPPORT); + } if (unp->unp_vnode != NULL) return (EINVAL); namelen = soun->sun_len - offsetof(struct sockaddr_un, sun_path); if (namelen <= 0) - return EINVAL; - strncpy(buf, soun->sun_path, namelen); - buf[namelen] = 0; /* null-terminate the string */ + return (EINVAL); + + strlcpy(buf, soun->sun_path, namelen+1); NDINIT(&nd, CREATE, FOLLOW | LOCKPARENT, UIO_SYSSPACE32, - CAST_USER_ADDR_T(buf), &context); -/* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */ + CAST_USER_ADDR_T(buf), ctx); + /* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */ error = namei(&nd); if (error) { return (error); @@ -642,7 +803,7 @@ unp_bind( vp = nd.ni_vp; if (vp != NULL) { - /* + /* * need to do this before the vnode_put of dvp * since we may have to release an fs_nodelock */ @@ -654,18 +815,28 @@ unp_bind( return (EADDRINUSE); } + VATTR_INIT(&va); + VATTR_SET(&va, va_type, VSOCK); + VATTR_SET(&va, va_mode, (ACCESSPERMS & ~p->p_fd->fd_cmask)); + +#if CONFIG_MACF_SOCKET + /* + * This is #if MAC_SOCKET, because it affects the connection rate + * of Unix domain dockets that is critical for server performance + */ + error = mac_vnode_check_create(ctx, + nd.ni_dvp, &nd.ni_cnd, &va); + + if (error == 0) +#endif /* MAC_SOCKET */ /* authorize before creating */ - error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, &context); + error = vnode_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx); if (!error) { - VATTR_INIT(&va); - VATTR_SET(&va, va_type, VSOCK); - VATTR_SET(&va, va_mode, (ACCESSPERMS & ~p->p_fd->fd_cmask)); - /* create the socket */ - error = vn_create(dvp, &vp, &nd.ni_cnd, &va, 0, &context); + error = vn_create(dvp, &vp, &nd.ni_cnd, &va, 0, ctx); } - + nameidone(&nd); vnode_put(dvp); @@ -681,32 +852,49 @@ unp_bind( return (0); } + +/* + * Returns: 0 Success + * EAFNOSUPPORT Address family not supported + * EINVAL Invalid argument + * ENOTSOCK Not a socket + * ECONNREFUSED Connection refused + * EPROTOTYPE Protocol wrong type for socket + * EISCONN Socket is connected + * unp_connect2:EPROTOTYPE Protocol wrong type for socket + * unp_connect2:EINVAL Invalid argument + * namei:??? [anything namei can return] + * vnode_authorize:???? [anything vnode_authorize can return] + * + * Notes: p at this point is the current process, as this function is + * only called by sosend(), sendfile(), and soconnectlock(). + */ static int -unp_connect( - struct socket *so, - struct sockaddr *nam, - struct proc *p) +unp_connect(struct socket *so, struct sockaddr *nam, __unused proc_t p) { struct sockaddr_un *soun = (struct sockaddr_un *)nam; struct vnode *vp; struct socket *so2, *so3; struct unpcb *unp, *unp2, *unp3; - struct vfs_context context; + vfs_context_t ctx = vfs_context_current(); int error, len; struct nameidata nd; char buf[SOCK_MAXADDRLEN]; - context.vc_proc = p; - context.vc_ucred = p->p_ucred; /* XXX kauth_cred_get() ??? proxy */ + if (nam->sa_family != 0 && nam->sa_family != AF_UNIX) { + return (EAFNOSUPPORT); + } + so2 = so3 = NULL; len = nam->sa_len - offsetof(struct sockaddr_un, sun_path); if (len <= 0) - return EINVAL; - strncpy(buf, soun->sun_path, len); - buf[len] = 0; + return (EINVAL); - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE32, CAST_USER_ADDR_T(buf), &context); + strlcpy(buf, soun->sun_path, len+1); + + NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE32, + CAST_USER_ADDR_T(buf), ctx); error = namei(&nd); if (error) { return (error); @@ -718,11 +906,11 @@ unp_connect( goto bad; } - error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, &context); + error = vnode_authorize(vp, NULL, KAUTH_VNODE_WRITE_DATA, ctx); if (error) goto bad; so2 = vp->v_socket; - if (so2 == 0) { + if (so2 == 0 || so2->so_pcb == NULL) { error = ECONNREFUSED; goto bad; } @@ -734,7 +922,7 @@ unp_connect( error = EPROTOTYPE; goto bad; } - + /* * Check if socket was connected while we were trying to * acquire the funnel. @@ -744,7 +932,7 @@ unp_connect( error = EISCONN; goto bad; } - + if (so->so_proto->pr_flags & PR_CONNREQUIRED) { if ((so2->so_options & SO_ACCEPTCONN) == 0 || (so3 = sonewconn(so2, 0, nam)) == 0) { @@ -756,8 +944,7 @@ unp_connect( unp3 = sotounpcb(so3); if (unp2->unp_addr) unp3->unp_addr = (struct sockaddr_un *) - dup_sockaddr((struct sockaddr *) - unp2->unp_addr, 1); + dup_sockaddr((struct sockaddr *)unp2->unp_addr, 1); /* * unp_peercred management: @@ -766,7 +953,7 @@ unp_connect( * from its process structure at the time of connect() * (which is now). */ - cru2x(p->p_ucred, &unp3->unp_peercred); + cru2x(vfs_context_ucred(ctx), &unp3->unp_peercred); unp3->unp_flags |= UNP_HAVEPC; /* * The receiver's (server's) credentials are copied @@ -778,9 +965,15 @@ unp_connect( KASSERT(unp2->unp_flags & UNP_HAVEPCCACHED, ("unp_connect: listener without cached peercred")); memcpy(&unp->unp_peercred, &unp2->unp_peercred, - sizeof(unp->unp_peercred)); + sizeof (unp->unp_peercred)); unp->unp_flags |= UNP_HAVEPC; +#if CONFIG_MACF_SOCKET + /* XXXMAC: recursive lock: SOCK_LOCK(so); */ + mac_socketpeer_label_associate_socket(so, so3); + mac_socketpeer_label_associate_socket(so3, so); + /* XXXMAC: SOCK_UNLOCK(so); */ +#endif /* MAC_SOCKET */ so2->so_usecount--; /* drop reference taken on so2 */ so2 = so3; so3->so_usecount++; /* make sure we keep it around */ @@ -793,10 +986,13 @@ bad: return (error); } +/* + * Returns: 0 Success + * EPROTOTYPE Protocol wrong type for socket + * EINVAL Invalid argument + */ int -unp_connect2( - struct socket *so, - struct socket *so2) +unp_connect2(struct socket *so, struct socket *so2) { struct unpcb *unp = sotounpcb(so); struct unpcb *unp2; @@ -818,14 +1014,15 @@ unp_connect2( break; case SOCK_STREAM: - /* This takes care of socketpair */ - if (!(unp->unp_flags & UNP_HAVEPC) && !(unp2->unp_flags & UNP_HAVEPC)) { - cru2x(kauth_cred_get(), &unp->unp_peercred); - unp->unp_flags |= UNP_HAVEPC; - - cru2x(kauth_cred_get(), &unp2->unp_peercred); - unp2->unp_flags |= UNP_HAVEPC; - } + /* This takes care of socketpair */ + if (!(unp->unp_flags & UNP_HAVEPC) && + !(unp2->unp_flags & UNP_HAVEPC)) { + cru2x(kauth_cred_get(), &unp->unp_peercred); + unp->unp_flags |= UNP_HAVEPC; + + cru2x(kauth_cred_get(), &unp2->unp_peercred); + unp2->unp_flags |= UNP_HAVEPC; + } unp2->unp_conn = unp; soisconnected(so); soisconnected(so2); @@ -844,18 +1041,19 @@ unp_disconnect(struct unpcb *unp) if (unp2 == 0) return; - lck_mtx_assert(unp_mutex, LCK_MTX_ASSERT_OWNED); - unp->unp_conn = 0; + unp->unp_conn = NULL; switch (unp->unp_socket->so_type) { case SOCK_DGRAM: + lck_rw_lock_exclusive(unp_list_mtx); LIST_REMOVE(unp, unp_reflink); + lck_rw_done(unp_list_mtx); unp->unp_socket->so_state &= ~SS_ISCONNECTED; break; case SOCK_STREAM: soisdisconnected(unp->unp_socket); - unp2->unp_conn = 0; + unp2->unp_conn = NULL; soisdisconnected(unp2->unp_socket); break; } @@ -873,13 +1071,14 @@ unp_abort(struct unpcb *unp) static int unp_pcblist SYSCTL_HANDLER_ARGS { +#pragma unused(oidp,arg2) int error, i, n; struct unpcb *unp, **unp_list; unp_gen_t gencnt; struct xunpgen xug; struct unp_head *head; - lck_mtx_lock(unp_mutex); + lck_rw_lock_shared(unp_list_mtx); head = ((intptr_t)arg1 == SOCK_DGRAM ? &unp_dhead : &unp_shead); /* @@ -888,15 +1087,15 @@ unp_pcblist SYSCTL_HANDLER_ARGS */ if (req->oldptr == USER_ADDR_NULL) { n = unp_count; - req->oldidx = 2 * (sizeof xug) - + (n + n/8) * sizeof(struct xunpcb); - lck_mtx_unlock(unp_mutex); - return 0; + req->oldidx = 2 * sizeof (xug) + (n + n / 8) * + sizeof (struct xunpcb); + lck_rw_done(unp_list_mtx); + return (0); } if (req->newptr != USER_ADDR_NULL) { - lck_mtx_unlock(unp_mutex); - return EPERM; + lck_rw_done(unp_list_mtx); + return (EPERM); } /* @@ -905,33 +1104,34 @@ unp_pcblist SYSCTL_HANDLER_ARGS gencnt = unp_gencnt; n = unp_count; - bzero(&xug, sizeof(xug)); - xug.xug_len = sizeof xug; + bzero(&xug, sizeof (xug)); + xug.xug_len = sizeof (xug); xug.xug_count = n; xug.xug_gen = gencnt; xug.xug_sogen = so_gencnt; - error = SYSCTL_OUT(req, &xug, sizeof xug); + error = SYSCTL_OUT(req, &xug, sizeof (xug)); if (error) { - lck_mtx_unlock(unp_mutex); - return error; + lck_rw_done(unp_list_mtx); + return (error); } /* * We are done if there is no pcb */ if (n == 0) { - lck_mtx_unlock(unp_mutex); - return 0; + lck_rw_done(unp_list_mtx); + return (0); } - MALLOC(unp_list, struct unpcb **, n * sizeof *unp_list, M_TEMP, M_WAITOK); + MALLOC(unp_list, struct unpcb **, n * sizeof (*unp_list), + M_TEMP, M_WAITOK); if (unp_list == 0) { - lck_mtx_unlock(unp_mutex); - return ENOMEM; + lck_rw_done(unp_list_mtx); + return (ENOMEM); } - + for (unp = head->lh_first, i = 0; unp && i < n; - unp = unp->unp_link.le_next) { + unp = unp->unp_link.le_next) { if (unp->unp_gencnt <= gencnt) unp_list[i++] = unp; } @@ -943,23 +1143,23 @@ unp_pcblist SYSCTL_HANDLER_ARGS if (unp->unp_gencnt <= gencnt) { struct xunpcb xu; - bzero(&xu, sizeof(xu)); - xu.xu_len = sizeof xu; + bzero(&xu, sizeof (xu)); + xu.xu_len = sizeof (xu); xu.xu_unpp = (struct unpcb_compat *)unp; /* * XXX - need more locking here to protect against * connect/disconnect races for SMP. */ if (unp->unp_addr) - bcopy(unp->unp_addr, &xu.xu_addr, - unp->unp_addr->sun_len); + bcopy(unp->unp_addr, &xu.xu_addr, + unp->unp_addr->sun_len); if (unp->unp_conn && unp->unp_conn->unp_addr) bcopy(unp->unp_conn->unp_addr, - &xu.xu_caddr, - unp->unp_conn->unp_addr->sun_len); - bcopy(unp, &xu.xu_unp, sizeof(xu.xu_unp)); + &xu.xu_caddr, + unp->unp_conn->unp_addr->sun_len); + bcopy(unp, &xu.xu_unp, sizeof (xu.xu_unp)); sotoxsocket(unp->unp_socket, &xu.xu_socket); - error = SYSCTL_OUT(req, &xu, sizeof xu); + error = SYSCTL_OUT(req, &xu, sizeof (xu)); } } if (!error) { @@ -970,22 +1170,22 @@ unp_pcblist SYSCTL_HANDLER_ARGS * while we were processing this request, and it * might be necessary to retry. */ - bzero(&xug, sizeof(xug)); - xug.xug_len = sizeof xug; + bzero(&xug, sizeof (xug)); + xug.xug_len = sizeof (xug); xug.xug_gen = unp_gencnt; xug.xug_sogen = so_gencnt; xug.xug_count = unp_count; - error = SYSCTL_OUT(req, &xug, sizeof xug); + error = SYSCTL_OUT(req, &xug, sizeof (xug)); } FREE(unp_list, M_TEMP); - lck_mtx_unlock(unp_mutex); - return error; + lck_rw_done(unp_list_mtx); + return (error); } -SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist, CTLFLAG_RD, +SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist, CTLFLAG_RD, (caddr_t)(long)SOCK_DGRAM, 0, unp_pcblist, "S,xunpcb", "List of active local datagram sockets"); -SYSCTL_PROC(_net_local_stream, OID_AUTO, pcblist, CTLFLAG_RD, +SYSCTL_PROC(_net_local_stream, OID_AUTO, pcblist, CTLFLAG_RD, (caddr_t)(long)SOCK_STREAM, 0, unp_pcblist, "S,xunpcb", "List of active local stream sockets"); @@ -1000,9 +1200,7 @@ unp_shutdown(struct unpcb *unp) } static void -unp_drop( - struct unpcb *unp, - int errno) +unp_drop(struct unpcb *unp, int errno) { struct socket *so = unp->unp_socket; @@ -1018,16 +1216,21 @@ unp_drain() } #endif +/* + * Returns: 0 Success + * EMSGSIZE The new fd's will not fit + * ENOBUFS Cannot alloc struct fileproc + */ int unp_externalize(struct mbuf *rights) { - struct proc *p = current_proc(); /* XXX */ + proc_t p = current_proc(); /* XXX */ int i; struct cmsghdr *cm = mtod(rights, struct cmsghdr *); struct fileglob **rp = (struct fileglob **)(cm + 1); struct fileproc *fp; struct fileglob *fg; - int newfds = (cm->cmsg_len - sizeof(*cm)) / sizeof (int); + int newfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int); int f; proc_fdlock(p); @@ -1039,30 +1242,45 @@ unp_externalize(struct mbuf *rights) for (i = 0; i < newfds; i++) { fg = *rp; unp_discard_fdlocked(fg, p); - *rp++ = 0; + *rp++ = NULL; } proc_fdunlock(p); return (EMSGSIZE); } /* - * now change each pointer to an fd in the global table to + * now change each pointer to an fd in the global table to * an integer that is the index to the local fd table entry * that we set up to point to the global one we are transferring. - * XXX this assumes a pointer and int are the same size...! + * XXX (1) this assumes a pointer and int are the same size...! + * XXX (2) allocation failures should be non-fatal */ for (i = 0; i < newfds; i++) { +#if CONFIG_MACF_SOCKET + /* + * If receive access is denied, don't pass along + * and error message, just discard the descriptor. + */ + if (mac_file_check_receive(kauth_cred_get(), *rp)) { + fg = *rp; + *rp++ = 0; + unp_discard_fdlocked(fg, p); + continue; + } +#endif if (fdalloc(p, 0, &f)) - panic("unp_externalize"); + panic("unp_externalize:fdalloc"); fg = *rp; - MALLOC_ZONE(fp, struct fileproc *, sizeof(struct fileproc), M_FILEPROC, M_WAITOK); - bzero(fp, sizeof(struct fileproc)); + MALLOC_ZONE(fp, struct fileproc *, sizeof (struct fileproc), + M_FILEPROC, M_WAITOK); + if (fp == NULL) + panic("unp_externalize: MALLOC_ZONE"); + bzero(fp, sizeof (struct fileproc)); fp->f_iocount = 0; fp->f_fglob = fg; - p->p_fd->fd_ofiles[f] = fp; fg_removeuipc(fg); - *fdflags(p, f) &= ~UF_RESERVED; - unp_rights--; + procfdtbl_releasefd(p, f, fp); + (void) OSAddAtomic(-1, (volatile SInt32 *)&unp_rights); *(int *)rp++ = f; } proc_fdunlock(p); @@ -1073,36 +1291,51 @@ unp_externalize(struct mbuf *rights) void unp_init(void) { - unp_zone = zinit(sizeof(struct unpcb), - (nmbclusters * sizeof(struct unpcb)), - 4096, "unpzone"); + unp_zone = zinit(sizeof (struct unpcb), + (nmbclusters * sizeof (struct unpcb)), 4096, "unpzone"); + if (unp_zone == 0) panic("unp_init"); LIST_INIT(&unp_dhead); LIST_INIT(&unp_shead); - - unp_mutex = localdomain.dom_mtx; + + /* + * allocate lock group attribute and group for udp pcb mutexes + */ + unp_mtx_grp_attr = lck_grp_attr_alloc_init(); + + unp_mtx_grp = lck_grp_alloc_init("unp_list", unp_mtx_grp_attr); + + unp_mtx_attr = lck_attr_alloc_init(); + + if ((unp_list_mtx = lck_rw_alloc_init(unp_mtx_grp, + unp_mtx_attr)) == NULL) + return; /* pretty much dead if this fails... */ + } #ifndef MIN -#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif +/* + * Returns: 0 Success + * EINVAL + * fdgetf_noref:EBADF + */ static int -unp_internalize( - struct mbuf *control, - struct proc *p) +unp_internalize(struct mbuf *control, proc_t p) { struct cmsghdr *cm = mtod(control, struct cmsghdr *); struct fileglob **rp; struct fileproc *fp; - register int i, error; + int i, error; int oldfds; - int fdgetf_noref(proc_t, struct fileglob **, struct fileproc **); + /* 64bit: cmsg_len is 'uint32_t', m_len is 'long' */ if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET || - cm->cmsg_len != control->m_len) { - return (EINVAL); + (unsigned long)cm->cmsg_len != (unsigned long)control->m_len) { + return (EINVAL); } oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int); @@ -1110,10 +1343,10 @@ unp_internalize( rp = (struct fileglob **)(cm + 1); for (i = 0; i < oldfds; i++) { - if (error = fdgetf_noref(p, *(int *)rp++, (struct fileglob **)0)) { - proc_fdunlock(p); - return (error); - } + if ((error = fdgetf_noref(p, *(int *)rp++, NULL)) != 0) { + proc_fdunlock(p); + return (error); + } } rp = (struct fileglob **)(cm + 1); @@ -1121,22 +1354,34 @@ unp_internalize( (void) fdgetf_noref(p, *(int *)rp, &fp); fg_insertuipc(fp->f_fglob); *rp++ = fp->f_fglob; - unp_rights++; + (void) OSAddAtomic(1, (volatile SInt32 *)&unp_rights); } proc_fdunlock(p); return (0); } -static int unp_defer, unp_gcing; +static int unp_defer, unp_gcing, unp_gcwait; + +/* always called under uipc_lock */ +void +unp_gc_wait(void) +{ + while (unp_gcing != 0) { + unp_gcwait = 1; + msleep(&unp_gcing, uipc_lock, 0 , "unp_gc_wait", NULL); + } +} + static void -unp_gc() +unp_gc(void) { - register struct fileglob *fg, *nextfg; - register struct socket *so; + struct fileglob *fg, *nextfg; + struct socket *so; struct fileglob **extra_ref, **fpp; int nunref, i; + int need_gcwakeup = 0; lck_mtx_lock(uipc_lock); if (unp_gcing) { @@ -1146,8 +1391,8 @@ unp_gc() unp_gcing = 1; unp_defer = 0; lck_mtx_unlock(uipc_lock); - /* - * before going through all this, set all FDs to + /* + * before going through all this, set all FDs to * be NOT defered and NOT externally accessible */ for (fg = fmsghead.lh_first; fg != 0; fg = fg->f_msglist.le_next) { @@ -1156,7 +1401,8 @@ unp_gc() lck_mtx_unlock(&fg->fg_lock); } do { - for (fg = fmsghead.lh_first; fg != 0; fg = fg->f_msglist.le_next) { + for (fg = fmsghead.lh_first; fg != 0; + fg = fg->f_msglist.le_next) { lck_mtx_lock(&fg->fg_lock); /* * If the file is not open, skip it @@ -1178,30 +1424,30 @@ unp_gc() * if it's not defered, then check if it's * already marked.. if so skip it */ - if (fg->fg_flag & FMARK){ + if (fg->fg_flag & FMARK) { lck_mtx_unlock(&fg->fg_lock); continue; } - /* + /* * If all references are from messages - * in transit, then skip it. it's not + * in transit, then skip it. it's not * externally accessible. - */ + */ if (fg->fg_count == fg->fg_msgcount) { lck_mtx_unlock(&fg->fg_lock); continue; } - /* + /* * If it got this far then it must be * externally accessible. */ fg->fg_flag |= FMARK; } /* - * either it was defered, or it is externally + * either it was defered, or it is externally * accessible and not already marked so. * Now check if it is possibly one of OUR sockets. - */ + */ if (fg->fg_type != DTYPE_SOCKET || (so = (struct socket *)fg->fg_data) == 0) { lck_mtx_unlock(&fg->fg_lock); @@ -1213,7 +1459,10 @@ unp_gc() continue; } #ifdef notdef - /* if this code is enabled need to run under network funnel */ + /* + * if this code is enabled need to run + * under network funnel + */ if (so->so_rcv.sb_flags & SB_LOCK) { /* * This is problematical; it's not clear @@ -1233,7 +1482,7 @@ unp_gc() * So, Ok, it's one of our sockets and it IS externally * accessible (or was defered). Now we look * to see if we hold any file descriptors in its - * message buffers. Follow those links and mark them + * message buffers. Follow those links and mark them * as accessible too. */ unp_scan(so->so_rcv.sb_mb, unp_mark); @@ -1279,20 +1528,21 @@ unp_gc() * * 91/09/19, bsy@cs.cmu.edu */ - extra_ref = _MALLOC(nfiles * sizeof(struct fileglob *), M_FILEGLOB, M_WAITOK); + extra_ref = _MALLOC(nfiles * sizeof (struct fileglob *), + M_FILEGLOB, M_WAITOK); for (nunref = 0, fg = fmsghead.lh_first, fpp = extra_ref; fg != 0; fg = nextfg) { lck_mtx_lock(&fg->fg_lock); nextfg = fg->f_msglist.le_next; - /* + /* * If it's not open, skip it */ if (fg->fg_count == 0) { lck_mtx_unlock(&fg->fg_lock); continue; } - /* + /* * If all refs are from msgs, and it's not marked accessible * then it must be referenced from some unreachable cycle * of (shut-down) FDs, so include it in our @@ -1305,7 +1555,7 @@ unp_gc() } lck_mtx_unlock(&fg->fg_lock); } - /* + /* * for each FD on our hit list, do the following two things */ for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp) { @@ -1314,41 +1564,63 @@ unp_gc() tfg = *fpp; if (tfg->fg_type == DTYPE_SOCKET && tfg->fg_data != NULL) { - sorflush((struct socket *)(tfg->fg_data)); + int locked = 0; + + so = (struct socket *)(tfg->fg_data); + + /* XXXX */ + /* Assume local sockets use a global lock */ + if (so->so_proto->pr_domain->dom_family != PF_LOCAL) { + socket_lock(so, 0); + locked = 1; + } + sorflush(so); + + if (locked) + socket_unlock(so, 0); } } for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp) - closef_locked((struct fileproc *)0, *fpp, (struct proc *) NULL); + closef_locked((struct fileproc *)0, *fpp, (proc_t)NULL); + + lck_mtx_lock(uipc_lock); unp_gcing = 0; - FREE((caddr_t)extra_ref, M_FILEGLOB); + if (unp_gcwait != 0) { + unp_gcwait = 0; + need_gcwakeup = 1; + } + lck_mtx_unlock(uipc_lock); + + if (need_gcwakeup != 0) + wakeup(&unp_gcing); + FREE((caddr_t)extra_ref, M_FILEGLOB); } void unp_dispose(struct mbuf *m) { - if (m) { unp_scan(m, unp_discard); } } +/* + * Returns: 0 Success + */ static int -unp_listen( - struct unpcb *unp, - struct proc *p) +unp_listen(struct unpcb *unp, proc_t p) { - - cru2x(p->p_ucred, &unp->unp_peercred); + kauth_cred_t safecred = kauth_cred_proc_ref(p); + cru2x(safecred, &unp->unp_peercred); + kauth_cred_unref(&safecred); unp->unp_flags |= UNP_HAVEPCCACHED; return (0); } -/* should run under kernel funnel */ +/* should run under kernel funnel */ static void -unp_scan( - struct mbuf *m0, - void (*op)(struct fileglob *)) +unp_scan(struct mbuf *m0, void (*op)(struct fileglob *)) { struct mbuf *m; struct fileglob **rp; @@ -1359,13 +1631,13 @@ unp_scan( while (m0) { for (m = m0; m; m = m->m_next) if (m->m_type == MT_CONTROL && - (size_t) m->m_len >= sizeof(*cm)) { + (size_t)m->m_len >= sizeof (*cm)) { cm = mtod(m, struct cmsghdr *); if (cm->cmsg_level != SOL_SOCKET || cm->cmsg_type != SCM_RIGHTS) continue; - qfds = (cm->cmsg_len - sizeof *cm) - / sizeof (struct fileglob *); + qfds = (cm->cmsg_len - sizeof (*cm)) / + sizeof (struct fileglob *); rp = (struct fileglob **)(cm + 1); for (i = 0; i < qfds; i++) (*op)(*rp++); @@ -1379,38 +1651,36 @@ unp_scan( static void unp_mark(struct fileglob *fg) { - lck_mtx_lock(&fg->fg_lock); + lck_mtx_lock(&fg->fg_lock); if (fg->fg_flag & FMARK) { - lck_mtx_unlock(&fg->fg_lock); + lck_mtx_unlock(&fg->fg_lock); return; } fg->fg_flag |= (FMARK|FDEFER); - lck_mtx_unlock(&fg->fg_lock); + lck_mtx_unlock(&fg->fg_lock); unp_defer++; } /* should run under kernel funnel */ static void -unp_discard(fg) - struct fileglob *fg; +unp_discard(struct fileglob *fg) { - struct proc *p = current_proc(); /* XXX */ + proc_t p = current_proc(); /* XXX */ + + (void) OSAddAtomic(1, (volatile SInt32 *)&unp_disposed); proc_fdlock(p); unp_discard_fdlocked(fg, p); proc_fdunlock(p); } static void -unp_discard_fdlocked(fg, p) - struct fileglob *fg; - struct proc *p; +unp_discard_fdlocked(struct fileglob *fg, proc_t p) { - fg_removeuipc(fg); - unp_rights--; + (void) OSAddAtomic(-1, (volatile SInt32 *)&unp_rights); (void) closef_locked((struct fileproc *)0, fg, p); }