aboutsummaryrefslogtreecommitdiff
path: root/sys/netinet6/udp6_usrreq.c
diff options
context:
space:
mode:
authorMichael Tuexen <tuexen@FreeBSD.org>2019-10-24 20:05:10 +0000
committerMichael Tuexen <tuexen@FreeBSD.org>2019-10-24 20:05:10 +0000
commit4a91aa8fc9b626834dec65629decf48d1e3d5036 (patch)
tree7f7d35c9f5479e6b8f45f7dee6699db4faab8520 /sys/netinet6/udp6_usrreq.c
parente9b148a3185f41e3a09e91ea75cae7828d908845 (diff)
downloadsrc-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.c31
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);