diff options
author | Michael Tuexen <tuexen@FreeBSD.org> | 2019-10-24 20:05:10 +0000 |
---|---|---|
committer | Michael Tuexen <tuexen@FreeBSD.org> | 2019-10-24 20:05:10 +0000 |
commit | 4a91aa8fc9b626834dec65629decf48d1e3d5036 (patch) | |
tree | 7f7d35c9f5479e6b8f45f7dee6699db4faab8520 /sys/netinet6/udp6_usrreq.c | |
parent | e9b148a3185f41e3a09e91ea75cae7828d908845 (diff) | |
download | src-4a91aa8fc9b626834dec65629decf48d1e3d5036.tar.gz src-4a91aa8fc9b626834dec65629decf48d1e3d5036.zip |
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.
Notes
Notes:
svn path=/head/; revision=354044
Diffstat (limited to 'sys/netinet6/udp6_usrreq.c')
-rw-r--r-- | sys/netinet6/udp6_usrreq.c | 31 |
1 files changed, 27 insertions, 4 deletions
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c index 6a46dedc1dfb..737dd2d759f6 100644 --- a/sys/netinet6/udp6_usrreq.c +++ b/sys/netinet6/udp6_usrreq.c @@ -1143,6 +1143,7 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td) 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, struct thread *td) 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, struct thread *td) #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 thread *td) 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, struct thread *td) 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, struct thread *td) 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); |