X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/b0d623f7f2ae71ed96e60569f61f9a9a27016e80..3e170ce000f1506b7b5d2c5c7faec85ceabb573d:/bsd/kern/uipc_usrreq.c diff --git a/bsd/kern/uipc_usrreq.c b/bsd/kern/uipc_usrreq.c index 26f38c8f5..cfe63ef28 100644 --- a/bsd/kern/uipc_usrreq.c +++ b/bsd/kern/uipc_usrreq.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2008 Apple Inc. All rights reserved. + * Copyright (c) 2000-2014 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -73,6 +73,7 @@ #include #include /* XXX must be before */ #include +#include #include #include #include @@ -88,6 +89,7 @@ #include #include #include +#include #include #include @@ -96,6 +98,13 @@ #include #endif /* CONFIG_MACF */ +#include + +/* + * 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 @@ -117,6 +126,32 @@ static u_int disconnect_in_progress; extern lck_mtx_t *uipc_lock; static struct unp_head unp_shead, unp_dhead; +/* + * mDNSResponder tracing. When enabled, endpoints connected to + * /var/run/mDNSResponder will be traced; during each send on + * the traced socket, we log the PID and process name of the + * sending process. We also print out a bit of info related + * to the data itself; this assumes ipc_msg_hdr in dnssd_ipc.h + * of mDNSResponder stays the same. + */ +#define MDNSRESPONDER_PATH "/var/run/mDNSResponder" + +static int unpst_tracemdns; /* enable tracing */ + +#define MDNS_IPC_MSG_HDR_VERSION_1 1 + +struct mdns_ipc_msg_hdr { + uint32_t version; + uint32_t datalen; + uint32_t ipc_flags; + uint32_t op; + union { + void *context; + uint32_t u32[2]; + } __attribute__((packed)); + uint32_t reg_index; +} __attribute__((packed)); + /* * Unix communications domain. * @@ -137,10 +172,9 @@ static void unp_disconnect(struct unpcb *); 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 *); @@ -271,7 +305,7 @@ uipc_detach(struct socket *so) if (unp == 0) return (EINVAL); - lck_mtx_assert(unp->unp_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(&unp->unp_mtx, LCK_MTX_ASSERT_OWNED); unp_detach(unp); return (0); } @@ -428,7 +462,8 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, } so2 = unp->unp_conn->unp_socket; - unp_get_locks_in_order(so, so2); + if (so != so2) + unp_get_locks_in_order(so, so2); if (unp->unp_addr) from = (struct sockaddr *)unp->unp_addr; @@ -450,7 +485,8 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, control = NULL; } - socket_unlock(so2, 1); + if (so != so2) + socket_unlock(so2, 1); m = NULL; if (nam) @@ -498,6 +534,16 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, break; } + if (unp->unp_flags & UNP_TRACE_MDNS) { + struct mdns_ipc_msg_hdr hdr; + + if (mbuf_copydata(m, 0, sizeof (hdr), &hdr) == 0 && + hdr.version == ntohl(MDNS_IPC_MSG_HDR_VERSION_1)) { + printf("%s[mDNSResponder] pid=%d (%s): op=0x%x\n", + __func__, p->p_pid, p->p_comm, ntohl(hdr.op)); + } + } + /* * Send to paired receive port, and then reduce send buffer * hiwater marks to maintain backpressure. Wake up readers. @@ -512,7 +558,12 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, 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; @@ -634,18 +685,32 @@ uipc_sockaddr(struct socket *so, struct sockaddr **nam) } 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: @@ -661,6 +726,43 @@ uipc_ctloutput(struct socket *so, struct sockopt *sopt) 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; @@ -671,6 +773,7 @@ uipc_ctloutput(struct socket *so, struct sockopt *sopt) error = EOPNOTSUPP; break; } + return (error); } @@ -694,17 +797,19 @@ 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, +SYSCTL_INT(_net_local_stream, OID_AUTO, sendspace, CTLFLAG_RW | CTLFLAG_LOCKED, &unpst_sendspace, 0, ""); -SYSCTL_INT(_net_local_stream, OID_AUTO, recvspace, CTLFLAG_RW, +SYSCTL_INT(_net_local_stream, OID_AUTO, recvspace, CTLFLAG_RW | CTLFLAG_LOCKED, &unpst_recvspace, 0, ""); +SYSCTL_INT(_net_local_stream, OID_AUTO, tracemdns, CTLFLAG_RW | CTLFLAG_LOCKED, + &unpst_tracemdns, 0, ""); SYSCTL_DECL(_net_local_dgram); -SYSCTL_INT(_net_local_dgram, OID_AUTO, maxdgram, CTLFLAG_RW, +SYSCTL_INT(_net_local_dgram, OID_AUTO, maxdgram, CTLFLAG_RW | CTLFLAG_LOCKED, &unpdg_sendspace, 0, ""); -SYSCTL_INT(_net_local_dgram, OID_AUTO, recvspace, CTLFLAG_RW, +SYSCTL_INT(_net_local_dgram, OID_AUTO, recvspace, CTLFLAG_RW | CTLFLAG_LOCKED, &unpdg_recvspace, 0, ""); SYSCTL_DECL(_net_local); -SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0, ""); +SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD | CTLFLAG_LOCKED, &unp_rights, 0, ""); /* * Returns: 0 Success @@ -739,11 +844,8 @@ unp_attach(struct socket *so) return (ENOBUFS); bzero(unp, sizeof (*unp)); - unp->unp_mtx = lck_mtx_alloc_init(unp_mtx_grp, unp_mtx_attr); - if (unp->unp_mtx == NULL) { - zfree(unp_zone, unp); - return(ENOBUFS); - } + lck_mtx_init(&unp->unp_mtx, + unp_mtx_grp, unp_mtx_attr); lck_rw_lock_exclusive(unp_list_mtx); LIST_INIT(&unp->unp_refs); @@ -780,8 +882,12 @@ unp_attach(struct socket *so) static void unp_detach(struct unpcb *unp) { + int so_locked = 1; + 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; @@ -805,13 +911,46 @@ unp_detach(struct unpcb *unp) if (unp->unp_conn) unp_disconnect(unp); while (unp->unp_refs.lh_first) { - struct unpcb *unp2 = unp->unp_refs.lh_first; - socket_unlock(unp->unp_socket, 0); - - socket_lock(unp2->unp_socket, 1); - unp_drop(unp2, ECONNRESET); - socket_unlock(unp2->unp_socket, 1); + struct unpcb *unp2 = NULL; + + /* This datagram socket is connected to one or more + * sockets. In order to avoid a race condition between removing + * this reference and closing the connected socket, we need + * to check disconnect_in_progress + */ + if (so_locked == 1) { + socket_unlock(unp->unp_socket, 0); + so_locked = 0; + } + lck_mtx_lock(unp_disconnect_lock); + while (disconnect_in_progress != 0) { + (void)msleep((caddr_t)&disconnect_in_progress, unp_disconnect_lock, + PSOCK, "disconnect", NULL); + } + disconnect_in_progress = 1; + lck_mtx_unlock(unp_disconnect_lock); + + /* Now we are sure that any unpcb socket disconnect is not happening */ + if (unp->unp_refs.lh_first != NULL) { + unp2 = unp->unp_refs.lh_first; + socket_lock(unp2->unp_socket, 1); + } + + lck_mtx_lock(unp_disconnect_lock); + disconnect_in_progress = 0; + wakeup(&disconnect_in_progress); + lck_mtx_unlock(unp_disconnect_lock); + + if (unp2 != NULL) { + /* We already locked this socket and have a reference on it */ + unp_drop(unp2, ECONNRESET); + socket_unlock(unp2->unp_socket, 1); + } + } + + if (so_locked == 0) { socket_lock(unp->unp_socket, 0); + so_locked = 1; } soisdisconnected(unp->unp_socket); /* makes sure we're getting dealloced */ @@ -857,7 +996,7 @@ unp_bind( socket_unlock(so, 0); strlcpy(buf, soun->sun_path, namelen+1); - NDINIT(&nd, CREATE, FOLLOW | LOCKPARENT, UIO_SYSSPACE, + 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 */ error = namei(&nd); @@ -903,7 +1042,7 @@ unp_bind( if (!error) { /* create the socket */ - error = vn_create(dvp, &vp, &nd.ni_cnd, &va, 0, ctx); + error = vn_create(dvp, &vp, &nd, &va, 0, 0, NULL, ctx); } nameidone(&nd); @@ -966,7 +1105,7 @@ unp_connect(struct socket *so, struct sockaddr *nam, __unused proc_t p) strlcpy(buf, soun->sun_path, len+1); socket_unlock(so, 0); - NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, + NDINIT(&nd, LOOKUP, OP_LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, CAST_USER_ADDR_T(buf), ctx); error = namei(&nd); if (error) { @@ -1011,8 +1150,13 @@ unp_connect(struct socket *so, struct sockaddr *nam, __unused proc_t p) if (so2->so_pcb == NULL) { error = ECONNREFUSED; - socket_unlock(so2, 1); - socket_lock(so, 0); + if (so != so2) { + socket_unlock(so2, 1); + socket_lock(so, 0); + } else { + /* Release the reference held for the listen socket */ + so2->so_usecount--; + } goto out; } @@ -1020,7 +1164,7 @@ unp_connect(struct socket *so, struct sockaddr *nam, __unused proc_t p) socket_unlock(so2, 0); socket_lock(so, 0); socket_lock(so2, 0); - } else { + } else if (so > so2) { socket_lock(so, 0); } /* @@ -1029,15 +1173,13 @@ unp_connect(struct socket *so, struct sockaddr *nam, __unused proc_t p) * XXX - probably shouldn't return an error for SOCK_DGRAM */ if ((so->so_state & SS_ISCONNECTED) != 0) { - socket_unlock(so2, 1); error = EISCONN; - goto out; + goto decref_out; } if (so->so_type != so2->so_type) { - socket_unlock(so2, 1); error = EPROTOTYPE; - goto out; + goto decref_out; } if (so->so_proto->pr_flags & PR_CONNREQUIRED) { @@ -1047,8 +1189,16 @@ unp_connect(struct socket *so, struct sockaddr *nam, __unused proc_t p) 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); @@ -1114,19 +1264,41 @@ unp_connect(struct socket *so, struct sockaddr *nam, __unused proc_t p) socket_lock(so3, 1); so2 = so3; + /* + * Enable tracing for mDNSResponder endpoints. (The use + * of sizeof instead of strlen below takes the null + * terminating character into account.) + */ + if (unpst_tracemdns && + !strncmp(soun->sun_path, MDNSRESPONDER_PATH, + sizeof (MDNSRESPONDER_PATH))) { + unp->unp_flags |= UNP_TRACE_MDNS; + unp2->unp_flags |= UNP_TRACE_MDNS; + } } error = unp_connect2(so, so2); + +decref_out: if (so2 != NULL) { - socket_unlock(so2, 1); + if (so != so2) { + socket_unlock(so2, 1); + } else { + /* Release the extra reference held for the listen socket. + * This is possible only for SOCK_DGRAM sockets. We refuse + * connecting to the same socket for SOCK_STREAM sockets. + */ + so2->so_usecount--; + } } if (list_so != NULL) { socket_lock(list_so, 0); socket_unlock(list_so, 1); } + out: - lck_mtx_assert(unp->unp_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(&unp->unp_mtx, LCK_MTX_ASSERT_OWNED); vnode_put(vp); return (error); } @@ -1147,8 +1319,8 @@ unp_connect2(struct socket *so, struct socket *so2) unp2 = sotounpcb(so2); - lck_mtx_assert(unp->unp_mtx, LCK_MTX_ASSERT_OWNED); - lck_mtx_assert(unp2->unp_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(&unp->unp_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(&unp2->unp_mtx, LCK_MTX_ASSERT_OWNED); /* Verify both sockets are still opened */ if (unp == 0 || unp2 == 0) @@ -1160,19 +1332,20 @@ unp_connect2(struct socket *so, struct socket *so2) switch (so->so_type) { case SOCK_DGRAM: - lck_rw_lock_exclusive(unp_list_mtx); LIST_INSERT_HEAD(&unp2->unp_refs, unp, unp_reflink); - lck_rw_done(unp_list_mtx); - - /* Avoid lock order reversals due to drop/acquire in soisconnected. */ - /* Keep an extra reference on so2 that will be dropped - * soon after getting the locks in order - */ - socket_unlock(so2, 0); - soisconnected(so); - unp_get_locks_in_order(so, so2); - so2->so_usecount--; + if (so != so2) { + /* Avoid lock order reversals due to drop/acquire in soisconnected. */ + /* Keep an extra reference on so2 that will be dropped + * soon after getting the locks in order + */ + socket_unlock(so2, 0); + soisconnected(so); + unp_get_locks_in_order(so, so2); + so2->so_usecount--; + } else { + soisconnected(so); + } break; @@ -1209,8 +1382,8 @@ unp_connect2(struct socket *so, struct socket *so2) default: panic("unknown socket type %d in unp_connect2", so->so_type); } - lck_mtx_assert(unp->unp_mtx, LCK_MTX_ASSERT_OWNED); - lck_mtx_assert(unp2->unp_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(&unp->unp_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(&unp2->unp_mtx, LCK_MTX_ASSERT_OWNED); return (0); } @@ -1251,7 +1424,12 @@ unp_disconnect(struct unpcb *unp) so2 = unp2->unp_socket; try_again: - if (so < so2) { + if (so == so2) { + if (so_locked == 0) { + socket_lock(so, 0); + } + waitso = so; + } else if (so < so2) { if (so_locked == 0) { socket_lock(so, 0); } @@ -1265,19 +1443,22 @@ try_again: socket_lock(so, 0); waitso = so; } + so_locked = 1; - lck_mtx_assert(unp->unp_mtx, LCK_MTX_ASSERT_OWNED); - lck_mtx_assert(unp2->unp_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(&unp->unp_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(&unp2->unp_mtx, LCK_MTX_ASSERT_OWNED); /* Check for the UNP_DONTDISCONNECT flag, if it * is set, release both sockets and go to sleep */ if ((((struct unpcb *)waitso->so_pcb)->unp_flags & UNP_DONTDISCONNECT) != 0) { - socket_unlock(so2, 1); + if (so != so2) { + socket_unlock(so2, 1); + } so_locked = 0; - (void)msleep(waitso->so_pcb, unp->unp_mtx, + (void)msleep(waitso->so_pcb, &unp->unp_mtx, PSOCK | PDROP, "unpdisconnect", NULL); goto try_again; } @@ -1289,14 +1470,16 @@ try_again: unp->unp_conn = NULL; so2->so_usecount--; + if (unp->unp_flags & UNP_TRACE_MDNS) + unp->unp_flags &= ~UNP_TRACE_MDNS; + 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; - socket_unlock(so2, 1); + if (so != so2) + socket_unlock(so2, 1); break; case SOCK_STREAM: @@ -1312,6 +1495,10 @@ try_again: unp2->unp_socket->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING); unp->unp_socket->so_state |= (SS_CANTRCVMORE|SS_CANTSENDMORE|SS_ISDISCONNECTED); + + if (unp2->unp_flags & UNP_TRACE_MDNS) + unp2->unp_flags &= ~UNP_TRACE_MDNS; + strdisconn = 1; break; default: @@ -1331,7 +1518,7 @@ out: socket_lock(so,0); soisdisconnected(so); } - lck_mtx_assert(unp->unp_mtx, LCK_MTX_ASSERT_OWNED); + lck_mtx_assert(&unp->unp_mtx, LCK_MTX_ASSERT_OWNED); return; } @@ -1343,31 +1530,37 @@ static void 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; @@ -1451,7 +1644,7 @@ unp_pcblist SYSCTL_HANDLER_ARGS 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. @@ -1488,14 +1681,15 @@ unp_pcblist SYSCTL_HANDLER_ARGS return (error); } -SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist, CTLFLAG_RD, +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, +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 @@ -1575,20 +1769,24 @@ 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; @@ -1631,14 +1829,15 @@ unp_pcblist64 SYSCTL_HANDLER_ARGS return (error); } -SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist64, CTLFLAG_RD, +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, +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) @@ -1676,9 +1875,16 @@ unp_externalize(struct mbuf *rights) 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); @@ -1686,14 +1892,9 @@ unp_externalize(struct mbuf *rights) * 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 @@ -1709,36 +1910,55 @@ unp_externalize(struct mbuf *rights) * 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"); @@ -1787,6 +2007,7 @@ unp_internalize(struct mbuf *control, proc_t p) 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 || @@ -1794,6 +2015,7 @@ unp_internalize(struct mbuf *control, proc_t p) return (EINVAL); } oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int); + bzero(fg_ins, sizeof(fg_ins)); proc_fdlock(p); fds = (int *)(cm + 1); @@ -1803,9 +2025,14 @@ unp_internalize(struct mbuf *control, proc_t p) 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); @@ -1815,12 +2042,20 @@ unp_internalize(struct mbuf *control, proc_t p) */ 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); } @@ -1917,21 +2152,17 @@ unp_gc(void) * 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 @@ -1959,7 +2190,7 @@ unp_gc(void) */ 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); /* @@ -2038,7 +2269,8 @@ unp_gc(void) 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); @@ -2071,7 +2303,7 @@ void unp_dispose(struct mbuf *m) { if (m) { - unp_scan(m, unp_discard); + unp_scan(m, unp_discard, NULL); } } @@ -2088,9 +2320,8 @@ unp_listen(struct unpcb *unp, proc_t p) 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; @@ -2110,16 +2341,15 @@ unp_scan(struct mbuf *m0, void (*op)(struct fileglob *)) 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); @@ -2134,25 +2364,22 @@ unp_mark(struct fileglob *fg) 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 @@ -2164,7 +2391,7 @@ unp_lock(struct socket *so, int refcount, void * lr) else lr_saved = lr; if (so->so_pcb) { - lck_mtx_lock(((struct unpcb *)so->so_pcb)->unp_mtx); + lck_mtx_lock(&((struct unpcb *)so->so_pcb)->unp_mtx); } else { panic("unp_lock: so=%p NO PCB! lr=%p ref=0x%x\n", so, lr_saved, so->so_usecount); @@ -2201,7 +2428,7 @@ unp_unlock(struct socket *so, int refcount, void * lr) if (so->so_pcb == NULL) { panic("unp_unlock: so=%p NO PCB usecount=%x\n", so, so->so_usecount); } else { - mutex_held = ((struct unpcb *)so->so_pcb)->unp_mtx; + mutex_held = &((struct unpcb *)so->so_pcb)->unp_mtx; } lck_mtx_assert(mutex_held, LCK_MTX_ASSERT_OWNED); so->unlock_lr[so->next_unlock_lr] = lr_saved; @@ -2214,12 +2441,9 @@ unp_unlock(struct socket *so, int refcount, void * lr) FREE(unp->unp_addr, M_SONAME); lck_mtx_unlock(mutex_held); - if (unp->unp_mtx) - lck_mtx_free(unp->unp_mtx, unp_mtx_grp); - unp->unp_gencnt = ++unp_gencnt; + lck_mtx_destroy(&unp->unp_mtx, unp_mtx_grp); zfree(unp_zone, unp); - --unp_count; unp_gc(); } else { @@ -2238,7 +2462,7 @@ unp_getlock(struct socket *so, __unused int locktype) if (so->so_pcb) { if (so->so_usecount < 0) panic("unp_getlock: so=%p usecount=%x\n", so, so->so_usecount); - return(unp->unp_mtx); + return(&unp->unp_mtx); } else { panic("unp_getlock: so=%p NULL so_pcb\n", so); return (so->so_proto->pr_domain->dom_mtx);