From owner-svn-src-all@freebsd.org Thu Oct 24 20:05:11 2019 Return-Path: Delivered-To: svn-src-all@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id C3F85159241; Thu, 24 Oct 2019 20:05:11 +0000 (UTC) (envelope-from tuexen@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 46zdWq4pH0z4Yks; Thu, 24 Oct 2019 20:05:11 +0000 (UTC) (envelope-from tuexen@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 841611B66A; Thu, 24 Oct 2019 20:05:11 +0000 (UTC) (envelope-from tuexen@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id x9OK5B13037386; Thu, 24 Oct 2019 20:05:11 GMT (envelope-from tuexen@FreeBSD.org) Received: (from tuexen@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id x9OK5AFp037383; Thu, 24 Oct 2019 20:05:10 GMT (envelope-from tuexen@FreeBSD.org) Message-Id: <201910242005.x9OK5AFp037383@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: tuexen set sender to tuexen@FreeBSD.org using -f From: Michael Tuexen Date: Thu, 24 Oct 2019 20:05:10 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r354044 - in head/sys: netinet netinet6 X-SVN-Group: head X-SVN-Commit-Author: tuexen X-SVN-Commit-Paths: in head/sys: netinet netinet6 X-SVN-Commit-Revision: 354044 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Thu, 24 Oct 2019 20:05:11 -0000 Author: tuexen Date: Thu Oct 24 20:05:10 2019 New Revision: 354044 URL: https://svnweb.freebsd.org/changeset/base/354044 Log: Ensure that the flags indicating IPv4/IPv6 are not changed by failing bind() calls. This would lead to inconsistent state resulting in a panic. A fix for stable/11 was committed in https://svnweb.freebsd.org/base?view=revision&revision=338986 An accelerated MFC is planned as discussed with emaste@. Reported by: syzbot+2609a378d89264ff5a42@syzkaller.appspotmail.com Obtained from: jtl@ MFC after: 1 day Sponsored by: Netflix, Inc. Modified: head/sys/netinet/tcp_usrreq.c head/sys/netinet6/sctp6_usrreq.c head/sys/netinet6/udp6_usrreq.c Modified: head/sys/netinet/tcp_usrreq.c ============================================================================== --- head/sys/netinet/tcp_usrreq.c Thu Oct 24 20:02:48 2019 (r354043) +++ head/sys/netinet/tcp_usrreq.c Thu Oct 24 20:05:10 2019 (r354044) @@ -347,6 +347,7 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct inpcb *inp; struct tcpcb *tp = NULL; struct sockaddr_in6 *sin6; + u_char vflagsav; sin6 = (struct sockaddr_in6 *)nam; if (nam->sa_len != sizeof (*sin6)) @@ -363,6 +364,7 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, inp = sotoinpcb(so); KASSERT(inp != NULL, ("tcp6_usr_bind: inp == NULL")); INP_WLOCK(inp); + vflagsav = inp->inp_vflag; if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { error = EINVAL; goto out; @@ -397,6 +399,8 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, error = in6_pcbbind(inp, nam, td->td_ucred); INP_HASH_WUNLOCK(&V_tcbinfo); out: + if (error != 0) + inp->inp_vflag = vflagsav; TCPDEBUG2(PRU_BIND); TCP_PROBE2(debug__user, tp, PRU_BIND); INP_WUNLOCK(inp); @@ -459,6 +463,7 @@ tcp6_usr_listen(struct socket *so, int backlog, struct int error = 0; struct inpcb *inp; struct tcpcb *tp = NULL; + u_char vflagsav; TCPDEBUG0; inp = sotoinpcb(so); @@ -468,6 +473,7 @@ tcp6_usr_listen(struct socket *so, int backlog, struct error = EINVAL; goto out; } + vflagsav = inp->inp_vflag; tp = intotcpcb(inp); TCPDEBUG1(); SOCK_LOCK(so); @@ -493,6 +499,9 @@ tcp6_usr_listen(struct socket *so, int backlog, struct if (IS_FASTOPEN(tp->t_flags)) tp->t_tfo_pending = tcp_fastopen_alloc_counter(); + if (error != 0) + inp->inp_vflag = vflagsav; + out: TCPDEBUG2(PRU_LISTEN); TCP_PROBE2(debug__user, tp, PRU_LISTEN); @@ -569,6 +578,8 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *n struct inpcb *inp; struct tcpcb *tp = NULL; struct sockaddr_in6 *sin6; + u_int8_t incflagsav; + u_char vflagsav; TCPDEBUG0; @@ -585,6 +596,8 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *n inp = sotoinpcb(so); KASSERT(inp != NULL, ("tcp6_usr_connect: inp == NULL")); INP_WLOCK(inp); + vflagsav = inp->inp_vflag; + incflagsav = inp->inp_inc.inc_flags; if (inp->inp_flags & INP_TIMEWAIT) { error = EADDRINUSE; goto out; @@ -618,11 +631,11 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *n error = EAFNOSUPPORT; goto out; } - inp->inp_vflag |= INP_IPV4; - inp->inp_vflag &= ~INP_IPV6; if ((error = prison_remote_ip4(td->td_ucred, &sin.sin_addr)) != 0) goto out; + inp->inp_vflag |= INP_IPV4; + inp->inp_vflag &= ~INP_IPV6; if ((error = tcp_connect(tp, (struct sockaddr *)&sin, td)) != 0) goto out; #ifdef TCP_OFFLOAD @@ -640,11 +653,11 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *n } } #endif + if ((error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr)) != 0) + goto out; inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; inp->inp_inc.inc_flags |= INC_ISIPV6; - if ((error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr)) != 0) - goto out; if ((error = tcp6_connect(tp, nam, td)) != 0) goto out; #ifdef TCP_OFFLOAD @@ -657,6 +670,15 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *n error = tp->t_fb->tfb_tcp_output(tp); out: + /* + * If the implicit bind in the connect call fails, restore + * the flags we modified. + */ + if (error != 0 && inp->inp_lport == 0) { + inp->inp_vflag = vflagsav; + inp->inp_inc.inc_flags = incflagsav; + } + TCPDEBUG2(PRU_CONNECT); TCP_PROBE2(debug__user, tp, PRU_CONNECT); INP_WUNLOCK(inp); @@ -912,6 +934,9 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf #ifdef INET6 int isipv6; #endif + u_int8_t incflagsav; + u_char vflagsav; + bool restoreflags; TCPDEBUG0; /* @@ -923,6 +948,9 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf inp = sotoinpcb(so); KASSERT(inp != NULL, ("tcp_usr_send: inp == NULL")); INP_WLOCK(inp); + vflagsav = inp->inp_vflag; + incflagsav = inp->inp_inc.inc_flags; + restoreflags = false; if (inp->inp_flags & (INP_TIMEWAIT | INP_DROPPED)) { if (control) m_freem(control); @@ -1003,6 +1031,7 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf m_freem(m); goto out; } + restoreflags = true; inp->inp_vflag &= ~INP_IPV6; sinp = &sin; in6_sin6_2_sin(sinp, sin6); @@ -1033,6 +1062,7 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf error = EAFNOSUPPORT; goto out; } + restoreflags = true; inp->inp_vflag &= ~INP_IPV4; inp->inp_inc.inc_flags |= INC_ISIPV6; if ((error = prison_remote_ip6(td->td_ucred, @@ -1083,6 +1113,14 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf error = tcp_connect(tp, (struct sockaddr *)sinp, td); #endif + /* + * The bind operation in tcp_connect succeeded. We + * no longer want to restore the flags if later + * operations fail. + */ + if (error == 0 || inp->inp_lport != 0) + restoreflags = false; + if (error) goto out; if (IS_FASTOPEN(tp->t_flags)) @@ -1153,6 +1191,14 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf error = tcp_connect(tp, (struct sockaddr *)sinp, td); #endif + /* + * The bind operation in tcp_connect succeeded. We + * no longer want to restore the flags if later + * operations fail. + */ + if (error == 0 || inp->inp_lport != 0) + restoreflags = false; + if (error) goto out; tp->snd_wnd = TTCP_CLIENT_SND_WND; @@ -1171,6 +1217,14 @@ tcp_usr_send(struct socket *so, int flags, struct mbuf TCP_LOG_USERSEND, error, 0, NULL, false); out: + /* + * If the request was unsuccessful and we changed flags, + * restore the original flags. + */ + if (error != 0 && restoreflags) { + inp->inp_vflag = vflagsav; + inp->inp_inc.inc_flags = incflagsav; + } TCPDEBUG2((flags & PRUS_OOB) ? PRU_SENDOOB : ((flags & PRUS_EOF) ? PRU_SEND_EOF : PRU_SEND)); TCP_PROBE2(debug__user, tp, (flags & PRUS_OOB) ? PRU_SENDOOB : Modified: head/sys/netinet6/sctp6_usrreq.c ============================================================================== --- head/sys/netinet6/sctp6_usrreq.c Thu Oct 24 20:02:48 2019 (r354043) +++ head/sys/netinet6/sctp6_usrreq.c Thu Oct 24 20:05:10 2019 (r354044) @@ -558,6 +558,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, s { struct sctp_inpcb *inp; int error; + u_char vflagsav; inp = (struct sctp_inpcb *)so->so_pcb; if (inp == NULL) { @@ -588,6 +589,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, s return (EINVAL); } } + vflagsav = inp->ip_inp.inp.inp_vflag; inp->ip_inp.inp.inp_vflag &= ~INP_IPV4; inp->ip_inp.inp.inp_vflag |= INP_IPV6; if ((addr != NULL) && (SCTP_IPV6_V6ONLY(inp) == 0)) { @@ -617,7 +619,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, s inp->ip_inp.inp.inp_vflag |= INP_IPV4; inp->ip_inp.inp.inp_vflag &= ~INP_IPV6; error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, NULL, p); - return (error); + goto out; } #endif break; @@ -634,7 +636,8 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, s if (addr->sa_family == AF_INET) { /* can't bind v4 addr to v6 only socket! */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return (EINVAL); + error = EINVAL; + goto out; } #endif sin6_p = (struct sockaddr_in6 *)addr; @@ -643,10 +646,14 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, s /* can't bind v4-mapped addrs either! */ /* NOTE: we don't support SIIT */ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP6_USRREQ, EINVAL); - return (EINVAL); + error = EINVAL; + goto out; } } error = sctp_inpcb_bind(so, addr, NULL, p); +out: + if (error != 0) + inp->ip_inp.inp.inp_vflag = vflagsav; return (error); } Modified: head/sys/netinet6/udp6_usrreq.c ============================================================================== --- head/sys/netinet6/udp6_usrreq.c Thu Oct 24 20:02:48 2019 (r354043) +++ head/sys/netinet6/udp6_usrreq.c Thu Oct 24 20:05:10 2019 (r354044) @@ -1143,6 +1143,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, str struct inpcb *inp; struct inpcbinfo *pcbinfo; int error; + u_char vflagsav; pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol); inp = sotoinpcb(so); @@ -1150,6 +1151,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, str INP_WLOCK(inp); INP_HASH_WLOCK(pcbinfo); + vflagsav = inp->inp_vflag; inp->inp_vflag &= ~INP_IPV4; inp->inp_vflag |= INP_IPV6; if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) { @@ -1177,6 +1179,8 @@ udp6_bind(struct socket *so, struct sockaddr *nam, str #ifdef INET out: #endif + if (error != 0) + inp->inp_vflag = vflagsav; INP_HASH_WUNLOCK(pcbinfo); INP_WUNLOCK(inp); return (error); @@ -1223,6 +1227,7 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct inpcbinfo *pcbinfo; struct sockaddr_in6 *sin6; int error; + u_char vflagsav; pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol); inp = sotoinpcb(so); @@ -1250,17 +1255,26 @@ udp6_connect(struct socket *so, struct sockaddr *nam, goto out; } in6_sin6_2_sin(&sin, sin6); - inp->inp_vflag |= INP_IPV4; - inp->inp_vflag &= ~INP_IPV6; error = prison_remote_ip4(td->td_ucred, &sin.sin_addr); if (error != 0) goto out; + vflagsav = inp->inp_vflag; + inp->inp_vflag |= INP_IPV4; + inp->inp_vflag &= ~INP_IPV6; INP_HASH_WLOCK(pcbinfo); error = in_pcbconnect(inp, (struct sockaddr *)&sin, td->td_ucred); INP_HASH_WUNLOCK(pcbinfo); + /* + * If connect succeeds, mark socket as connected. If + * connect fails and socket is unbound, reset inp_vflag + * field. + */ if (error == 0) soisconnected(so); + else if (inp->inp_laddr.s_addr == INADDR_ANY && + inp->inp_lport == 0) + inp->inp_vflag = vflagsav; goto out; } else { if ((inp->inp_vflag & INP_IPV6) == 0) { @@ -1273,16 +1287,25 @@ udp6_connect(struct socket *so, struct sockaddr *nam, error = EISCONN; goto out; } - inp->inp_vflag &= ~INP_IPV4; - inp->inp_vflag |= INP_IPV6; error = prison_remote_ip6(td->td_ucred, &sin6->sin6_addr); if (error != 0) goto out; + vflagsav = inp->inp_vflag; + inp->inp_vflag &= ~INP_IPV4; + inp->inp_vflag |= INP_IPV6; INP_HASH_WLOCK(pcbinfo); error = in6_pcbconnect(inp, nam, td->td_ucred); INP_HASH_WUNLOCK(pcbinfo); + /* + * If connect succeeds, mark socket as connected. If + * connect fails and socket is unbound, reset inp_vflag + * field. + */ if (error == 0) soisconnected(so); + else if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) && + inp->inp_lport == 0) + inp->inp_vflag = vflagsav; out: INP_WUNLOCK(inp); return (error);