/*
- * Copyright (c) 2000-2010 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2015 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <sys/fcntl.h>
#include <sys/malloc.h> /* XXX must be before <sys/file.h> */
#include <sys/file_internal.h>
+#include <sys/guarded.h>
#include <sys/filedesc.h>
#include <sys/lock.h>
#include <sys/mbuf.h>
#include <sys/unpcb.h>
#include <sys/vnode_internal.h>
#include <sys/kdebug.h>
+#include <sys/mcache.h>
#include <kern/zalloc.h>
#include <kern/locks.h>
#include <security/mac_framework.h>
#endif /* CONFIG_MACF */
+#include <mach/vm_param.h>
+
+/*
+ * Maximum number of FDs that can be passed in an mbuf
+ */
+#define UIPC_MAX_CMSG_FD 512
+
#define f_msgcount f_fglob->fg_msgcount
#define f_cred f_fglob->fg_cred
#define f_ops f_fglob->fg_ops
static void unp_shutdown(struct unpcb *);
static void unp_drop(struct unpcb *, int);
__private_extern__ 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 void unp_scan(struct mbuf *, void (*)(struct fileglob *, void *arg), void *arg);
+static void unp_mark(struct fileglob *, __unused void *);
+static void unp_discard(struct fileglob *, void *);
static int unp_internalize(struct mbuf *, proc_t);
static int unp_listen(struct unpcb *, proc_t);
static void unpcb_to_compat(struct unpcb *, struct unpcb_compat *);
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;
+ if ((int32_t)snd->sb_hiwat >=
+ (int32_t)(rcv->sb_cc - unp->unp_conn->unp_cc)) {
+ snd->sb_hiwat -= rcv->sb_cc - unp->unp_conn->unp_cc;
+ } else {
+ snd->sb_hiwat = 0;
+ }
unp->unp_conn->unp_cc = rcv->sb_cc;
if (didreceive) {
control = NULL;
}
struct pr_usrreqs uipc_usrreqs = {
- uipc_abort, uipc_accept, uipc_attach, uipc_bind, uipc_connect,
- uipc_connect2, pru_control_notsupp, uipc_detach, uipc_disconnect,
- uipc_listen, uipc_peeraddr, uipc_rcvd, pru_rcvoob_notsupp,
- uipc_send, uipc_sense, uipc_shutdown, uipc_sockaddr,
- sosend, soreceive, pru_sopoll_notsupp
+ .pru_abort = uipc_abort,
+ .pru_accept = uipc_accept,
+ .pru_attach = uipc_attach,
+ .pru_bind = uipc_bind,
+ .pru_connect = uipc_connect,
+ .pru_connect2 = uipc_connect2,
+ .pru_detach = uipc_detach,
+ .pru_disconnect = uipc_disconnect,
+ .pru_listen = uipc_listen,
+ .pru_peeraddr = uipc_peeraddr,
+ .pru_rcvd = uipc_rcvd,
+ .pru_send = uipc_send,
+ .pru_sense = uipc_sense,
+ .pru_shutdown = uipc_shutdown,
+ .pru_sockaddr = uipc_sockaddr,
+ .pru_sosend = sosend,
+ .pru_soreceive = soreceive,
};
int
uipc_ctloutput(struct socket *so, struct sockopt *sopt)
{
struct unpcb *unp = sotounpcb(so);
- int error;
+ int error = 0;
+ pid_t peerpid;
+ struct socket *peerso;
switch (sopt->sopt_dir) {
case SOPT_GET:
error = EINVAL;
}
break;
+ case LOCAL_PEERPID:
+ case LOCAL_PEEREPID:
+ if (unp->unp_conn == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ peerso = unp->unp_conn->unp_socket;
+ if (peerso == NULL)
+ panic("peer is connected but has no socket?");
+ unp_get_locks_in_order(so, peerso);
+ if (sopt->sopt_name == LOCAL_PEEREPID &&
+ peerso->so_flags & SOF_DELEGATED)
+ peerpid = peerso->e_pid;
+ else
+ peerpid = peerso->last_pid;
+ socket_unlock(peerso, 1);
+ error = sooptcopyout(sopt, &peerpid, sizeof (peerpid));
+ break;
+ case LOCAL_PEERUUID:
+ case LOCAL_PEEREUUID:
+ if (unp->unp_conn == NULL) {
+ error = ENOTCONN;
+ break;
+ }
+ peerso = unp->unp_conn->unp_socket;
+ if (peerso == NULL)
+ panic("peer is connected but has no socket?");
+ unp_get_locks_in_order(so, peerso);
+ if (sopt->sopt_name == LOCAL_PEEREUUID &&
+ peerso->so_flags & SOF_DELEGATED)
+ error = sooptcopyout(sopt, &peerso->e_uuid,
+ sizeof (peerso->e_uuid));
+ else
+ error = sooptcopyout(sopt, &peerso->last_uuid,
+ sizeof (peerso->last_uuid));
+ socket_unlock(peerso, 1);
+ break;
default:
error = EOPNOTSUPP;
break;
error = EOPNOTSUPP;
break;
}
+
return (error);
}
lck_rw_lock_exclusive(unp_list_mtx);
LIST_REMOVE(unp, unp_link);
+ --unp_count;
+ ++unp_gencnt;
lck_rw_done(unp_list_mtx);
if (unp->unp_vnode) {
struct vnode *tvp = NULL;
namelen = soun->sun_len - offsetof(struct sockaddr_un, sun_path);
if (namelen <= 0)
return (EINVAL);
-
+ /*
+ * Note: sun_path is not a zero terminated "C" string
+ */
+ ASSERT(namelen < SOCK_MAXADDRLEN);
+ bcopy(soun->sun_path, buf, namelen);
+ buf[namelen] = 0;
+
socket_unlock(so, 0);
- strlcpy(buf, soun->sun_path, namelen+1);
NDINIT(&nd, CREATE, OP_MKFIFO, FOLLOW | LOCKPARENT, UIO_SYSSPACE,
CAST_USER_ADDR_T(buf), ctx);
/* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */
len = nam->sa_len - offsetof(struct sockaddr_un, sun_path);
if (len <= 0)
return (EINVAL);
+ /*
+ * Note: sun_path is not a zero terminated "C" string
+ */
+ ASSERT(len < SOCK_MAXADDRLEN);
+ bcopy(soun->sun_path, buf, len);
+ buf[len] = 0;
- strlcpy(buf, soun->sun_path, len+1);
socket_unlock(so, 0);
NDINIT(&nd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE,
if ((so2->so_options & SO_ACCEPTCONN) == 0 ||
(so3 = sonewconn(so2, 0, nam)) == 0) {
error = ECONNREFUSED;
- socket_unlock(so2, 1);
- socket_lock(so, 0);
+ if (so != so2) {
+ socket_unlock(so2, 1);
+ socket_lock(so, 0);
+ } else {
+ socket_lock(so, 0);
+ /* Release the reference held for
+ * listen socket.
+ */
+ so2->so_usecount--;
+ }
goto out;
}
unp2 = sotounpcb(so2);
unpcb_to_compat(struct unpcb *up, struct unpcb_compat *cp)
{
#if defined(__LP64__)
- cp->unp_link.le_next = (u_int32_t)(uintptr_t)up->unp_link.le_next;
- cp->unp_link.le_prev = (u_int32_t)(uintptr_t)up->unp_link.le_prev;
+ cp->unp_link.le_next = (u_int32_t)
+ VM_KERNEL_ADDRPERM(up->unp_link.le_next);
+ cp->unp_link.le_prev = (u_int32_t)
+ VM_KERNEL_ADDRPERM(up->unp_link.le_prev);
#else
- cp->unp_link.le_next = (struct unpcb_compat *)up->unp_link.le_next;
- cp->unp_link.le_prev = (struct unpcb_compat **)up->unp_link.le_prev;
+ cp->unp_link.le_next = (struct unpcb_compat *)
+ VM_KERNEL_ADDRPERM(up->unp_link.le_next);
+ cp->unp_link.le_prev = (struct unpcb_compat **)
+ VM_KERNEL_ADDRPERM(up->unp_link.le_prev);
#endif
- cp->unp_socket = (_UNPCB_PTR(struct socket *))(uintptr_t)up->unp_socket;
- cp->unp_vnode = (_UNPCB_PTR(struct vnode *))(uintptr_t)up->unp_vnode;
+ cp->unp_socket = (_UNPCB_PTR(struct socket *))
+ VM_KERNEL_ADDRPERM(up->unp_socket);
+ cp->unp_vnode = (_UNPCB_PTR(struct vnode *))
+ VM_KERNEL_ADDRPERM(up->unp_vnode);
cp->unp_ino = up->unp_ino;
cp->unp_conn = (_UNPCB_PTR(struct unpcb_compat *))
- (uintptr_t)up->unp_conn;
- cp->unp_refs = (u_int32_t)(uintptr_t)up->unp_refs.lh_first;
+ VM_KERNEL_ADDRPERM(up->unp_conn);
+ cp->unp_refs = (u_int32_t)VM_KERNEL_ADDRPERM(up->unp_refs.lh_first);
#if defined(__LP64__)
cp->unp_reflink.le_next =
- (u_int32_t)(uintptr_t)up->unp_reflink.le_next;
+ (u_int32_t)VM_KERNEL_ADDRPERM(up->unp_reflink.le_next);
cp->unp_reflink.le_prev =
- (u_int32_t)(uintptr_t)up->unp_reflink.le_prev;
+ (u_int32_t)VM_KERNEL_ADDRPERM(up->unp_reflink.le_prev);
#else
cp->unp_reflink.le_next =
- (struct unpcb_compat *)up->unp_reflink.le_next;
+ (struct unpcb_compat *)VM_KERNEL_ADDRPERM(up->unp_reflink.le_next);
cp->unp_reflink.le_prev =
- (struct unpcb_compat **)up->unp_reflink.le_prev;
+ (struct unpcb_compat **)VM_KERNEL_ADDRPERM(up->unp_reflink.le_prev);
#endif
cp->unp_addr = (_UNPCB_PTR(struct sockaddr_un *))
- (uintptr_t)up->unp_addr;
+ VM_KERNEL_ADDRPERM(up->unp_addr);
cp->unp_cc = up->unp_cc;
cp->unp_mbcnt = up->unp_mbcnt;
cp->unp_gencnt = up->unp_gencnt;
bzero(&xu, sizeof (xu));
xu.xu_len = sizeof (xu);
xu.xu_unpp = (_UNPCB_PTR(struct unpcb_compat *))
- (uintptr_t)unp;
+ VM_KERNEL_ADDRPERM(unp);
/*
* XXX - need more locking here to protect against
* connect/disconnect races for SMP.
return (error);
}
-SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist, CTLFLAG_RD | CTLFLAG_LOCKED,
+SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist,
+ CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED,
(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 | CTLFLAG_LOCKED,
+SYSCTL_PROC(_net_local_stream, OID_AUTO, pcblist,
+ CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED,
(caddr_t)(long)SOCK_STREAM, 0, unp_pcblist, "S,xunpcb",
"List of active local stream sockets");
-#if !CONFIG_EMBEDDED
static int
unp_pcblist64 SYSCTL_HANDLER_ARGS
bzero(&xu, xu_len);
xu.xu_len = xu_len;
- xu.xu_unpp = (u_int64_t)(uintptr_t)unp;
- xu.xunp_link.le_next =
- (u_int64_t)(uintptr_t)unp->unp_link.le_next;
- xu.xunp_link.le_prev =
- (u_int64_t)(uintptr_t)unp->unp_link.le_prev;
- xu.xunp_socket = (u_int64_t)(uintptr_t)unp->unp_socket;
- xu.xunp_vnode = (u_int64_t)(uintptr_t)unp->unp_vnode;
+ xu.xu_unpp = (u_int64_t)VM_KERNEL_ADDRPERM(unp);
+ xu.xunp_link.le_next = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_link.le_next);
+ xu.xunp_link.le_prev = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_link.le_prev);
+ xu.xunp_socket = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_socket);
+ xu.xunp_vnode = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_vnode);
xu.xunp_ino = unp->unp_ino;
- xu.xunp_conn = (u_int64_t)(uintptr_t)unp->unp_conn;
- xu.xunp_refs = (u_int64_t)(uintptr_t)unp->unp_refs.lh_first;
- xu.xunp_reflink.le_next =
- (u_int64_t)(uintptr_t)unp->unp_reflink.le_next;
- xu.xunp_reflink.le_prev =
- (u_int64_t)(uintptr_t)unp->unp_reflink.le_prev;
+ xu.xunp_conn = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_conn);
+ xu.xunp_refs = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_refs.lh_first);
+ xu.xunp_reflink.le_next = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_reflink.le_next);
+ xu.xunp_reflink.le_prev = (u_int64_t)
+ VM_KERNEL_ADDRPERM(unp->unp_reflink.le_prev);
xu.xunp_cc = unp->unp_cc;
xu.xunp_mbcnt = unp->unp_mbcnt;
xu.xunp_gencnt = unp->unp_gencnt;
return (error);
}
-SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist64, CTLFLAG_RD | CTLFLAG_LOCKED,
+SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist64,
+ CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED,
(caddr_t)(long)SOCK_DGRAM, 0, unp_pcblist64, "S,xunpcb64",
"List of active local datagram sockets 64 bit");
-SYSCTL_PROC(_net_local_stream, OID_AUTO, pcblist64, CTLFLAG_RD | CTLFLAG_LOCKED,
+SYSCTL_PROC(_net_local_stream, OID_AUTO, pcblist64,
+ CTLTYPE_STRUCT | CTLFLAG_RD | CTLFLAG_LOCKED,
(caddr_t)(long)SOCK_STREAM, 0, unp_pcblist64, "S,xunpcb64",
"List of active local stream sockets 64 bit");
-#endif /* !CONFIG_EMBEDDED */
static void
unp_shutdown(struct unpcb *unp)
struct fileglob **rp = (struct fileglob **)(cm + 1);
int *fds = (int *)(cm + 1);
struct fileproc *fp;
- struct fileglob *fg;
+ struct fileglob **fgl;
int newfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
- int f;
+ int f, error = 0;
+
+ MALLOC(fgl, struct fileglob **, newfds * sizeof (struct fileglob *),
+ M_TEMP, M_WAITOK);
+ if (fgl == NULL) {
+ error = ENOMEM;
+ goto discard;
+ }
proc_fdlock(p);
* if the new FD's will not fit, then we free them all
*/
if (!fdavail(p, newfds)) {
- for (i = 0; i < newfds; i++) {
- fg = *rp;
- unp_discard_fdlocked(fg, p);
- *rp++ = NULL;
- }
proc_fdunlock(p);
-
- return (EMSGSIZE);
+ error = EMSGSIZE;
+ goto discard;
}
/*
* now change each pointer to an fd in the global table to
* 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);
+ if (mac_file_check_receive(kauth_cred_get(), rp[i])) {
+ proc_fdunlock(p);
+ unp_discard(rp[i], p);
+ fds[i] = 0;
+ proc_fdlock(p);
continue;
}
#endif
if (fdalloc(p, 0, &f))
panic("unp_externalize:fdalloc");
- fg = rp[i];
- MALLOC_ZONE(fp, struct fileproc *, sizeof (struct fileproc),
- M_FILEPROC, M_WAITOK);
+ fp = fileproc_alloc_init(NULL);
if (fp == NULL)
panic("unp_externalize: MALLOC_ZONE");
- bzero(fp, sizeof (struct fileproc));
fp->f_iocount = 0;
- fp->f_fglob = fg;
- fg_removeuipc(fg);
+ fp->f_fglob = rp[i];
+ if (fg_removeuipc_mark(rp[i]))
+ fgl[i] = rp[i];
+ else
+ fgl[i] = NULL;
procfdtbl_releasefd(p, f, fp);
- (void) OSAddAtomic(-1, &unp_rights);
fds[i] = f;
}
proc_fdunlock(p);
- return (0);
+ for (i = 0; i < newfds; i++) {
+ if (fgl[i] != NULL) {
+ VERIFY(fgl[i]->fg_lflags & FG_RMMSGQ);
+ fg_removeuipc(fgl[i]);
+ }
+ if (fds[i])
+ (void) OSAddAtomic(-1, &unp_rights);
+ }
+
+discard:
+ if (fgl)
+ FREE(fgl, M_TEMP);
+ if (error) {
+ for (i = 0; i < newfds; i++) {
+ unp_discard(*rp, p);
+ *rp++ = NULL;
+ }
+ }
+ return (error);
}
void
unp_init(void)
{
+ _CASSERT(UIPC_MAX_CMSG_FD >= (MCLBYTES / sizeof(int)));
unp_zone = zinit(sizeof (struct unpcb),
(nmbclusters * sizeof (struct unpcb)), 4096, "unpzone");
struct fileproc *fp;
int i, error;
int oldfds;
+ uint8_t fg_ins[UIPC_MAX_CMSG_FD / 8];
/* 64bit: cmsg_len is 'uint32_t', m_len is 'long' */
if (cm->cmsg_type != SCM_RIGHTS || cm->cmsg_level != SOL_SOCKET ||
return (EINVAL);
}
oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
+ bzero(fg_ins, sizeof(fg_ins));
proc_fdlock(p);
fds = (int *)(cm + 1);
if (((error = fdgetf_noref(p, fds[i], &tmpfp)) != 0)) {
proc_fdunlock(p);
return (error);
- } else if (!filetype_issendable(tmpfp->f_fglob->fg_type)) {
+ } else if (!file_issendable(p, tmpfp)) {
proc_fdunlock(p);
return (EINVAL);
+ } else if (FP_ISGUARDED(tmpfp, GUARD_SOCKET_IPC)) {
+ error = fp_guard_exception(p,
+ fds[i], tmpfp, kGUARD_EXC_SOCKET_IPC);
+ proc_fdunlock(p);
+ return (error);
}
}
rp = (struct fileglob **)(cm + 1);
*/
for (i = (oldfds - 1); i >= 0; i--) {
(void) fdgetf_noref(p, fds[i], &fp);
- fg_insertuipc(fp->f_fglob);
+ if (fg_insertuipc_mark(fp->f_fglob))
+ fg_ins[i / 8] |= 0x80 >> (i % 8);
rp[i] = fp->f_fglob;
- (void) OSAddAtomic(1, &unp_rights);
}
proc_fdunlock(p);
+ for (i = 0; i < oldfds; i++) {
+ if (fg_ins[i / 8] & (0x80 >> (i % 8))) {
+ VERIFY(rp[i]->fg_lflags & FG_INSMSGQ);
+ fg_insertuipc(rp[i]);
+ }
+ (void) OSAddAtomic(1, &unp_rights);
+ }
+
return (0);
}
* accessible and not already marked so.
* Now check if it is possibly one of OUR sockets.
*/
- if (fg->fg_type != DTYPE_SOCKET ||
+ if (FILEGLOB_DTYPE(fg) != DTYPE_SOCKET ||
(so = (struct socket *)fg->fg_data) == 0) {
lck_mtx_unlock(&fg->fg_lock);
continue;
}
- if (so->so_proto->pr_domain != &localdomain ||
+ if (so->so_proto->pr_domain != localdomain ||
(so->so_proto->pr_flags&PR_RIGHTS) == 0) {
lck_mtx_unlock(&fg->fg_lock);
continue;
}
#ifdef notdef
- /*
- * 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
*/
lck_mtx_unlock(&fg->fg_lock);
- unp_scan(so->so_rcv.sb_mb, unp_mark);
+ unp_scan(so->so_rcv.sb_mb, unp_mark, 0);
}
} while (unp_defer);
/*
tfg = *fpp;
- if (tfg->fg_type == DTYPE_SOCKET && tfg->fg_data != NULL) {
+ if (FILEGLOB_DTYPE(tfg) == DTYPE_SOCKET &&
+ tfg->fg_data != NULL) {
so = (struct socket *)(tfg->fg_data);
socket_lock(so, 0);
unp_dispose(struct mbuf *m)
{
if (m) {
- unp_scan(m, unp_discard);
+ unp_scan(m, unp_discard, NULL);
}
}
return (0);
}
-/* 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 *, void *arg), void *arg)
{
struct mbuf *m;
struct fileglob **rp;
sizeof (int);
rp = (struct fileglob **)(cm + 1);
for (i = 0; i < qfds; i++)
- (*op)(*rp++);
+ (*op)(*rp++, arg);
break; /* XXX, but saves time */
}
m0 = m0->m_act;
}
}
-/* should run under kernel funnel */
static void
-unp_mark(struct fileglob *fg)
+unp_mark(struct fileglob *fg, __unused void *arg)
{
lck_mtx_lock(&fg->fg_lock);
unp_defer++;
}
-/* should run under kernel funnel */
static void
-unp_discard(struct fileglob *fg)
+unp_discard(struct fileglob *fg, void *p)
{
- proc_t p = current_proc(); /* XXX */
+ if (p == NULL)
+ p = current_proc(); /* XXX */
(void) OSAddAtomic(1, &unp_disposed);
+ if (fg_removeuipc_mark(fg)) {
+ VERIFY(fg->fg_lflags & FG_RMMSGQ);
+ fg_removeuipc(fg);
+ }
+ (void) OSAddAtomic(-1, &unp_rights);
proc_fdlock(p);
- unp_discard_fdlocked(fg, p);
- proc_fdunlock(p);
-}
-static void
-unp_discard_fdlocked(struct fileglob *fg, proc_t p)
-{
- fg_removeuipc(fg);
-
- (void) OSAddAtomic(-1, &unp_rights);
(void) closef_locked((struct fileproc *)0, fg, p);
+ proc_fdunlock(p);
}
int
lck_mtx_unlock(mutex_held);
- unp->unp_gencnt = ++unp_gencnt;
+ lck_mtx_destroy(&unp->unp_mtx, unp_mtx_grp);
zfree(unp_zone, unp);
- --unp_count;
unp_gc();
} else {