Index: sys/kern/uipc_socket.c =================================================================== --- sys/kern/uipc_socket.c +++ sys/kern/uipc_socket.c @@ -418,12 +418,14 @@ * a feature to change class of an existing lock, so we use DUPOK. */ mtx_init(&so->so_lock, "socket", NULL, MTX_DEF | MTX_DUPOK); + so->so_snd.sb_mtx = &so->so_snd_mtx; + so->so_rcv.sb_mtx = &so->so_rcv_mtx; SOCKBUF_LOCK_INIT(&so->so_snd, "so_snd"); SOCKBUF_LOCK_INIT(&so->so_rcv, "so_rcv"); so->so_rcv.sb_sel = &so->so_rdsel; so->so_snd.sb_sel = &so->so_wrsel; - sx_init(&so->so_snd.sb_sx, "so_snd_sx"); - sx_init(&so->so_rcv.sb_sx, "so_rcv_sx"); + sx_init(&so->so_snd_sx, "so_snd_sx"); + sx_init(&so->so_rcv_sx, "so_rcv_sx"); TAILQ_INIT(&so->so_snd.sb_aiojobq); TAILQ_INIT(&so->so_rcv.sb_aiojobq); TASK_INIT(&so->so_snd.sb_aiotask, 0, soaio_snd, so); @@ -487,8 +489,8 @@ if (so->so_snd.sb_hiwat) (void)chgsbsize(so->so_cred->cr_uidinfo, &so->so_snd.sb_hiwat, 0, RLIM_INFINITY); - sx_destroy(&so->so_snd.sb_sx); - sx_destroy(&so->so_rcv.sb_sx); + sx_destroy(&so->so_snd_sx); + sx_destroy(&so->so_rcv_sx); SOCKBUF_LOCK_DESTROY(&so->so_snd); SOCKBUF_LOCK_DESTROY(&so->so_rcv); } @@ -899,18 +901,48 @@ return (error); } +/* + * Prepare for a call to solisten_proto(). Acquire all socket buffer locks in + * order to interlock with socket I/O. + */ int solisten_proto_check(struct socket *so) { - SOCK_LOCK_ASSERT(so); - if (so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING | - SS_ISDISCONNECTING)) + if ((so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING | + SS_ISDISCONNECTING)) != 0) return (EINVAL); + + /* + * Sleeping is not permitted here, so simply fail if userspace is + * attempting to transmit or receive on the socket. This kind of + * transient failure is not ideal, but it should occur only if userspace + * is misusing the socket interfaces. + */ + if (!sx_try_xlock(&so->so_snd_sx)) + return (EAGAIN); + if (!sx_try_xlock(&so->so_rcv_sx)) { + sx_xunlock(&so->so_snd_sx); + return (EAGAIN); + } + mtx_lock(&so->so_snd_mtx); + mtx_lock(&so->so_rcv_mtx); return (0); } +/* + * Undo the setup done by solisten_proto_check(). + */ +void +solisten_proto_abort(struct socket *so) +{ + mtx_unlock(&so->so_snd_mtx); + mtx_unlock(&so->so_rcv_mtx); + sx_xunlock(&so->so_snd_sx); + sx_xunlock(&so->so_rcv_sx); +} + void solisten_proto(struct socket *so, int backlog) { @@ -920,6 +952,9 @@ sbintime_t sbrcv_timeo, sbsnd_timeo; SOCK_LOCK_ASSERT(so); + KASSERT((so->so_state & (SS_ISCONNECTED | SS_ISCONNECTING | + SS_ISDISCONNECTING)) == 0, + ("%s: bad socket state %p", __func__, so)); if (SOLISTENING(so)) goto listening; @@ -938,10 +973,6 @@ sbdestroy(&so->so_snd, so); sbdestroy(&so->so_rcv, so); - sx_destroy(&so->so_snd.sb_sx); - sx_destroy(&so->so_rcv.sb_sx); - SOCKBUF_LOCK_DESTROY(&so->so_snd); - SOCKBUF_LOCK_DESTROY(&so->so_rcv); #ifdef INVARIANTS bzero(&so->so_rcv, @@ -974,6 +1005,11 @@ if (backlog < 0 || backlog > somaxconn) backlog = somaxconn; so->sol_qlimit = backlog; + + mtx_unlock(&so->so_snd_mtx); + mtx_unlock(&so->so_rcv_mtx); + sx_xunlock(&so->so_snd_sx); + sx_xunlock(&so->so_rcv_sx); } /* Index: sys/kern/uipc_usrreq.c =================================================================== --- sys/kern/uipc_usrreq.c +++ sys/kern/uipc_usrreq.c @@ -890,13 +890,17 @@ if (so->so_type != SOCK_STREAM && so->so_type != SOCK_SEQPACKET) return (EOPNOTSUPP); + /* + * Synchronize with concurrent connection attempts. + */ + error = 0; unp = sotounpcb(so); - KASSERT(unp != NULL, ("uipc_listen: unp == NULL")); - UNP_PCB_LOCK(unp); - if (unp->unp_vnode == NULL) { - /* Already connected or not bound to an address. */ - error = unp->unp_conn != NULL ? EINVAL : EDESTADDRREQ; + if (unp->unp_conn != NULL || (unp->unp_flags & UNP_CONNECTING) != 0) + error = EINVAL; + else if (unp->unp_vnode == NULL) + error = EDESTADDRREQ; + if (error != 0) { UNP_PCB_UNLOCK(unp); return (error); } @@ -1523,6 +1527,7 @@ bcopy(soun->sun_path, buf, len); buf[len] = 0; + error = 0; unp = sotounpcb(so); UNP_PCB_LOCK(unp); for (;;) { @@ -1538,13 +1543,16 @@ * lock the peer socket, to ensure that unp_conn cannot * transition between two valid sockets while locks are dropped. */ - if (unp->unp_conn != NULL) { - UNP_PCB_UNLOCK(unp); - return (EISCONN); + if (SOLISTENING(so)) + error = EOPNOTSUPP; + else if (unp->unp_conn != NULL) + error = EISCONN; + else if ((unp->unp_flags & UNP_CONNECTING) != 0) { + error = EALREADY; } - if ((unp->unp_flags & UNP_CONNECTING) != 0) { + if (error != 0) { UNP_PCB_UNLOCK(unp); - return (EALREADY); + return (error); } if (unp->unp_pairbusy > 0) { unp->unp_flags |= UNP_WAITING; Index: sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c =================================================================== --- sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c +++ sys/netgraph/bluetooth/socket/ng_btsocket_l2cap.c @@ -2506,14 +2506,17 @@ if (error != 0) goto out; if (pcb == NULL) { + solisten_proto_abort(so); error = EINVAL; goto out; } if (ng_btsocket_l2cap_node == NULL) { + solisten_proto_abort(so); error = EINVAL; goto out; } if (pcb->psm == 0) { + solisten_proto_abort(so); error = EADDRNOTAVAIL; goto out; } Index: sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c =================================================================== --- sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c +++ sys/netgraph/bluetooth/socket/ng_btsocket_rfcomm.c @@ -894,6 +894,7 @@ * from socreate() */ if (l2so == NULL) { + solisten_proto_abort(so); error = socreate_error; goto out; } @@ -907,8 +908,10 @@ */ error = ng_btsocket_rfcomm_session_create(&s, l2so, NG_HCI_BDADDR_ANY, NULL, td); - if (error != 0) + if (error != 0) { + solisten_proto_abort(so); goto out; + } l2so = NULL; } SOCK_LOCK(so); Index: sys/netinet/sctp_usrreq.c =================================================================== --- sys/netinet/sctp_usrreq.c +++ sys/netinet/sctp_usrreq.c @@ -7210,7 +7210,7 @@ } } } - SCTP_INP_RLOCK(inp); + SCTP_INP_WLOCK(inp); #ifdef SCTP_LOCK_LOGGING if (SCTP_BASE_SYSCTL(sctp_logging_level) & SCTP_LOCK_LOGGING_ENABLE) { sctp_log_lock(inp, (struct sctp_tcb *)NULL, SCTP_LOG_LOCK_SOCK); @@ -7218,9 +7218,9 @@ #endif SOCK_LOCK(so); error = solisten_proto_check(so); - SOCK_UNLOCK(so); if (error) { - SCTP_INP_RUNLOCK(inp); + SOCK_UNLOCK(so); + SCTP_INP_WUNLOCK(inp); return (error); } if ((sctp_is_feature_on(inp, SCTP_PCB_FLAGS_PORTREUSE)) && @@ -7232,7 +7232,9 @@ * move the guy that was listener to the TCP Pool. */ if (sctp_swap_inpcb_for_listen(inp)) { - SCTP_INP_RUNLOCK(inp); + SOCK_UNLOCK(so); + SCTP_INP_WUNLOCK(inp); + solisten_proto_abort(so); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EADDRINUSE); return (EADDRINUSE); } @@ -7240,25 +7242,31 @@ if ((inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE) && (inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED)) { - /* We are already connected AND the TCP model */ - SCTP_INP_RUNLOCK(inp); + SOCK_UNLOCK(so); + SCTP_INP_WUNLOCK(inp); + solisten_proto_abort(so); SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EADDRINUSE); return (EADDRINUSE); } - SCTP_INP_RUNLOCK(inp); if (inp->sctp_flags & SCTP_PCB_FLAGS_UNBOUND) { + SOCK_UNLOCK(so); + SCTP_INP_WUNLOCK(inp); /* We must do a bind. */ + /* XXXMJ LOR */ if ((error = sctp_inpcb_bind(so, NULL, NULL, p))) { + solisten_proto_abort(so); /* bind error, probably perm */ return (error); } + SCTP_INP_WLOCK(inp); + SOCK_LOCK(so); } - SCTP_INP_WLOCK(inp); if ((inp->sctp_flags & SCTP_PCB_FLAGS_UDPTYPE) == 0) { - SOCK_LOCK(so); solisten_proto(so, backlog); - SOCK_UNLOCK(so); + } else { + solisten_proto_abort(so); } + SOCK_UNLOCK(so); if (backlog > 0) { inp->sctp_flags |= SCTP_PCB_FLAGS_ACCEPTING; } else { Index: sys/netinet/tcp_usrreq.c =================================================================== --- sys/netinet/tcp_usrreq.c +++ sys/netinet/tcp_usrreq.c @@ -457,10 +457,15 @@ TCPDEBUG1(); SOCK_LOCK(so); error = solisten_proto_check(so); - INP_HASH_WLOCK(&V_tcbinfo); - if (error == 0 && inp->inp_lport == 0) - error = in_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); - INP_HASH_WUNLOCK(&V_tcbinfo); + if (error != 0) { + SOCK_UNLOCK(so); + goto out; + } + if (inp->inp_lport == 0) { + INP_HASH_WLOCK(&V_tcbinfo); + error = in_pcbbind(inp, NULL, td->td_ucred); + INP_HASH_WUNLOCK(&V_tcbinfo); + } if (error == 0) { tcp_state_change(tp, TCPS_LISTEN); solisten_proto(so, backlog); @@ -468,6 +473,8 @@ if ((so->so_options & SO_NO_OFFLOAD) == 0) tcp_offload_listen_start(tp); #endif + } else { + solisten_proto_abort(so); } SOCK_UNLOCK(so); @@ -504,12 +511,16 @@ TCPDEBUG1(); SOCK_LOCK(so); error = solisten_proto_check(so); + if (error != 0) { + SOCK_UNLOCK(so); + goto out; + } INP_HASH_WLOCK(&V_tcbinfo); - if (error == 0 && inp->inp_lport == 0) { + if (inp->inp_lport == 0) { inp->inp_vflag &= ~INP_IPV4; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) inp->inp_vflag |= INP_IPV4; - error = in6_pcbbind(inp, (struct sockaddr *)0, td->td_ucred); + error = in6_pcbbind(inp, NULL, td->td_ucred); } INP_HASH_WUNLOCK(&V_tcbinfo); if (error == 0) { @@ -519,6 +530,8 @@ if ((so->so_options & SO_NO_OFFLOAD) == 0) tcp_offload_listen_start(tp); #endif + } else { + solisten_proto_abort(so); } SOCK_UNLOCK(so); @@ -581,6 +594,10 @@ error = ECONNREFUSED; goto out; } + if (SOLISTENING(so)) { + error = EOPNOTSUPP; + goto out; + } tp = intotcpcb(inp); TCPDEBUG1(); NET_EPOCH_ENTER(et); @@ -643,6 +660,10 @@ error = ECONNREFUSED; goto out; } + if (SOLISTENING(so)) { + error = EINVAL; + goto out; + } tp = intotcpcb(inp); TCPDEBUG1(); #ifdef INET @@ -1021,6 +1042,10 @@ TCPDEBUG1(); if (nam != NULL && tp->t_state < TCPS_SYN_SENT) { + if (tp->t_state == TCPS_LISTEN) { + error = EINVAL; + goto out; + } switch (nam->sa_family) { #ifdef INET case AF_INET: Index: sys/sys/socketvar.h =================================================================== --- sys/sys/socketvar.h +++ sys/sys/socketvar.h @@ -453,6 +453,7 @@ void sohasoutofband(struct socket *so); int solisten(struct socket *so, int backlog, struct thread *td); void solisten_proto(struct socket *so, int backlog); +void solisten_proto_abort(struct socket *so); int solisten_proto_check(struct socket *so); int solisten_dequeue(struct socket *, struct socket **, int); struct socket *