Date: Thu, 27 Sep 2018 18:48:51 +0000 (UTC) From: Gordon Tetlow <gordon@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r338985 - in stable/10/sys: netinet netinet6 Message-ID: <201809271848.w8RImpcj079763@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: gordon Date: Thu Sep 27 18:48:50 2018 New Revision: 338985 URL: https://svnweb.freebsd.org/changeset/base/338985 Log: There are various cases where we modify the inp_vflag and inp_inc.inc_flags fields during a syscall, but don't restore those fields if the operation fails. This can leave the inp structure in an inconsistent state and cause various problems. Restore the inp_vflag and inp_inc.inc_flags fields when the underlying operation fails and the inp could be in an inconsistent state. This is a direct commit to the branch as the code is different enough in the other branches to make it difficult to resolve a merge. Submitted by: jtl@ Reported by: Jakub Jirasek, Secunia Research at Flexera Reviewed by: jhb@ Approved by: so Security: FreeBSD-EN-18:11.listen Security: CVE-2018-6925 Modified: stable/10/sys/netinet/tcp_usrreq.c stable/10/sys/netinet6/sctp6_usrreq.c stable/10/sys/netinet6/udp6_usrreq.c Modified: stable/10/sys/netinet/tcp_usrreq.c ============================================================================== --- stable/10/sys/netinet/tcp_usrreq.c Thu Sep 27 18:44:40 2018 (r338984) +++ stable/10/sys/netinet/tcp_usrreq.c Thu Sep 27 18:48:50 2018 (r338985) @@ -328,6 +328,7 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct inpcb *inp; struct tcpcb *tp = NULL; struct sockaddr_in6 *sin6p; + u_char vflagsav; sin6p = (struct sockaddr_in6 *)nam; if (nam->sa_len != sizeof (*sin6p)) @@ -344,6 +345,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; @@ -373,6 +375,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); INP_WUNLOCK(inp); return (error); @@ -434,6 +438,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); @@ -443,6 +448,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); @@ -469,6 +475,9 @@ tcp6_usr_listen(struct socket *so, int backlog, struct if (tp->t_flags & TF_FASTOPEN) tp->t_tfo_pending = tcp_fastopen_alloc_counter(); #endif + if (error != 0) + inp->inp_vflag = vflagsav; + out: TCPDEBUG2(PRU_LISTEN); INP_WUNLOCK(inp); @@ -543,6 +552,8 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *n struct inpcb *inp; struct tcpcb *tp = NULL; struct sockaddr_in6 *sin6p; + u_int8_t incflagsav; + u_char vflagsav; TCPDEBUG0; @@ -559,6 +570,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; @@ -584,11 +597,11 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *n } in6_sin6_2_sin(&sin, sin6p); - 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 @@ -601,11 +614,11 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *n goto out; } #endif + if ((error = prison_remote_ip6(td->td_ucred, &sin6p->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, &sin6p->sin6_addr)) != 0) - goto out; if ((error = tcp6_connect(tp, nam, td)) != 0) goto out; #ifdef TCP_OFFLOAD @@ -618,6 +631,15 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *n error = 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); INP_WUNLOCK(inp); return (error); Modified: stable/10/sys/netinet6/sctp6_usrreq.c ============================================================================== --- stable/10/sys/netinet6/sctp6_usrreq.c Thu Sep 27 18:44:40 2018 (r338984) +++ stable/10/sys/netinet6/sctp6_usrreq.c Thu Sep 27 18:48:50 2018 (r338985) @@ -608,6 +608,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, s struct sctp_inpcb *inp; struct in6pcb *inp6; int error; + u_char vflagsav; inp = (struct sctp_inpcb *)so->so_pcb; if (inp == NULL) { @@ -638,6 +639,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, s } } inp6 = (struct in6pcb *)inp; + vflagsav = inp6->inp_vflag; inp6->inp_vflag &= ~INP_IPV4; inp6->inp_vflag |= INP_IPV6; if ((addr != NULL) && (SCTP_IPV6_V6ONLY(inp6) == 0)) { @@ -667,7 +669,7 @@ sctp6_bind(struct socket *so, struct sockaddr *addr, s inp6->inp_vflag |= INP_IPV4; inp6->inp_vflag &= ~INP_IPV6; error = sctp_inpcb_bind(so, (struct sockaddr *)&sin, NULL, p); - return (error); + goto out; } #endif break; @@ -684,7 +686,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; @@ -693,10 +696,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) + inp6->inp_vflag = vflagsav; return (error); } Modified: stable/10/sys/netinet6/udp6_usrreq.c ============================================================================== --- stable/10/sys/netinet6/udp6_usrreq.c Thu Sep 27 18:44:40 2018 (r338984) +++ stable/10/sys/netinet6/udp6_usrreq.c Thu Sep 27 18:48:50 2018 (r338985) @@ -947,6 +947,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, str struct inpcb *inp; struct inpcbinfo *pcbinfo; int error; + u_char vflagsav; pcbinfo = get_inpcbinfo(so->so_proto->pr_protocol); inp = sotoinpcb(so); @@ -954,6 +955,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) { @@ -981,6 +983,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); @@ -1023,6 +1027,7 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct inpcbinfo *pcbinfo; struct sockaddr_in6 *sin6; int error; + u_char vflagsav; pcbinfo = get_inpcbinfo(so->so_proto->pr_protocol); inp = sotoinpcb(so); @@ -1046,17 +1051,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; } #endif @@ -1064,16 +1078,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);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201809271848.w8RImpcj079763>