aboutsummaryrefslogtreecommitdiff
path: root/sys/netinet
diff options
context:
space:
mode:
authorMark Johnston <markj@FreeBSD.org>2021-05-03 16:51:04 +0000
committerMark Johnston <markj@FreeBSD.org>2021-05-03 17:35:19 +0000
commitf161d294b92732df6254a89f393ab24999e122bf (patch)
tree3b0c4e481060d66ae8cdad006c1569e3ca4a5520 /sys/netinet
parenta3c7da3d08eec921f16a32c4c716b896385264e9 (diff)
downloadsrc-f161d294b92732df6254a89f393ab24999e122bf.tar.gz
src-f161d294b92732df6254a89f393ab24999e122bf.zip
Add missing sockaddr length and family validation to various protocols
Several protocol methods take a sockaddr as input. In some cases the sockaddr lengths were not being validated, or were validated after some out-of-bounds accesses could occur. Add requisite checking to various protocol entry points, and convert some existing checks to assertions where appropriate. Reported by: syzkaller+KASAN Reviewed by: tuexen, melifaro MFC after: 2 weeks Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D29519
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/in_pcb.c28
-rw-r--r--sys/netinet/ip_divert.c21
-rw-r--r--sys/netinet/raw_ip.c14
-rw-r--r--sys/netinet/sctp_usrreq.c24
-rw-r--r--sys/netinet/tcp_usrreq.c31
-rw-r--r--sys/netinet/udp_usrreq.c28
6 files changed, 101 insertions, 45 deletions
diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c
index 43eb31c4e31d..4c21bdbf1347 100644
--- a/sys/netinet/in_pcb.c
+++ b/sys/netinet/in_pcb.c
@@ -654,6 +654,10 @@ in_pcbbind(struct inpcb *inp, struct sockaddr *nam, struct ucred *cred)
{
int anonport, error;
+ KASSERT(nam == NULL || nam->sa_family == AF_INET,
+ ("%s: invalid address family for %p", __func__, nam));
+ KASSERT(nam == NULL || nam->sa_len == sizeof(struct sockaddr_in),
+ ("%s: invalid address length for %p", __func__, nam));
INP_WLOCK_ASSERT(inp);
INP_HASH_WLOCK_ASSERT(inp->inp_pcbinfo);
@@ -940,16 +944,11 @@ in_pcbbind_setup(struct inpcb *inp, struct sockaddr *nam, in_addr_t *laddrp,
return (error);
} else {
sin = (struct sockaddr_in *)nam;
- if (nam->sa_len != sizeof (*sin))
- return (EINVAL);
-#ifdef notdef
- /*
- * We should check the family, but old programs
- * incorrectly fail to initialize it.
- */
- if (sin->sin_family != AF_INET)
- return (EAFNOSUPPORT);
-#endif
+ KASSERT(sin->sin_family == AF_INET,
+ ("%s: invalid family for address %p", __func__, sin));
+ KASSERT(sin->sin_len == sizeof(*sin),
+ ("%s: invalid length for address %p", __func__, sin));
+
error = prison_local_ip4(cred, &sin->sin_addr);
if (error)
return (error);
@@ -1372,6 +1371,11 @@ in_pcbconnect_setup(struct inpcb *inp, struct sockaddr *nam,
u_short lport, fport;
int error;
+ KASSERT(sin->sin_family == AF_INET,
+ ("%s: invalid address family for %p", __func__, sin));
+ KASSERT(sin->sin_len == sizeof(*sin),
+ ("%s: invalid address length for %p", __func__, sin));
+
/*
* Because a global state change doesn't actually occur here, a read
* lock is sufficient.
@@ -1382,10 +1386,6 @@ in_pcbconnect_setup(struct inpcb *inp, struct sockaddr *nam,
if (oinpp != NULL)
*oinpp = NULL;
- if (nam->sa_len != sizeof (*sin))
- return (EINVAL);
- if (sin->sin_family != AF_INET)
- return (EAFNOSUPPORT);
if (sin->sin_port == 0)
return (EADDRNOTAVAIL);
laddr.s_addr = *laddrp;
diff --git a/sys/netinet/ip_divert.c b/sys/netinet/ip_divert.c
index c3f9c43b8f70..65974051ad1f 100644
--- a/sys/netinet/ip_divert.c
+++ b/sys/netinet/ip_divert.c
@@ -327,6 +327,22 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin,
struct ipfw_rule_ref *dt;
int error, family;
+ if (control) {
+ m_freem(control); /* XXX */
+ control = NULL;
+ }
+
+ if (sin != NULL) {
+ if (sin->sin_family != AF_INET) {
+ m_freem(m);
+ return (EAFNOSUPPORT);
+ }
+ if (sin->sin_len != sizeof(*sin)) {
+ m_freem(m);
+ return (EINVAL);
+ }
+ }
+
/*
* An mbuf may hasn't come from userland, but we pretend
* that it has.
@@ -335,9 +351,6 @@ div_output(struct socket *so, struct mbuf *m, struct sockaddr_in *sin,
m->m_nextpkt = NULL;
M_SETFIB(m, so->so_fibnum);
- if (control)
- m_freem(control); /* XXX */
-
mtag = m_tag_locate(m, MTAG_IPFW_RULE, 0, NULL);
if (mtag == NULL) {
/* this should be normal */
@@ -628,6 +641,8 @@ div_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
*/
if (nam->sa_family != AF_INET)
return EAFNOSUPPORT;
+ if (nam->sa_len != sizeof(struct sockaddr_in))
+ return EINVAL;
((struct sockaddr_in *)nam)->sin_addr.s_addr = INADDR_ANY;
INP_INFO_WLOCK(&V_divcbinfo);
INP_WLOCK(inp);
diff --git a/sys/netinet/raw_ip.c b/sys/netinet/raw_ip.c
index c9def015343c..89ab6a6bbdad 100644
--- a/sys/netinet/raw_ip.c
+++ b/sys/netinet/raw_ip.c
@@ -996,6 +996,8 @@ rip_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
struct inpcb *inp;
int error;
+ if (nam->sa_family != AF_INET)
+ return (EAFNOSUPPORT);
if (nam->sa_len != sizeof(*addr))
return (EINVAL);
@@ -1070,6 +1072,7 @@ rip_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
{
struct inpcb *inp;
u_long dst;
+ int error;
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("rip_send: inp == NULL"));
@@ -1084,9 +1087,16 @@ rip_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
}
dst = inp->inp_faddr.s_addr; /* Unlocked read. */
} else {
- if (nam == NULL) {
+ error = 0;
+ if (nam == NULL)
+ error = ENOTCONN;
+ else if (nam->sa_family != AF_INET)
+ error = EAFNOSUPPORT;
+ else if (nam->sa_len != sizeof(struct sockaddr_in))
+ error = EINVAL;
+ if (error != 0) {
m_freem(m);
- return (ENOTCONN);
+ return (error);
}
dst = ((struct sockaddr_in *)nam)->sin_addr.s_addr;
}
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
index 49cc6f22cc5a..238c20c0e368 100644
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -598,29 +598,27 @@ sctp_sendm(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
((inp->sctp_flags & SCTP_PCB_FLAGS_CONNECTED) ||
(inp->sctp_flags & SCTP_PCB_FLAGS_TCPTYPE))) {
goto connected_type;
- } else if (addr == NULL) {
+ }
+
+ error = 0;
+ if (addr == NULL) {
SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EDESTADDRREQ);
error = EDESTADDRREQ;
- sctp_m_freem(m);
- if (control) {
- sctp_m_freem(control);
- control = NULL;
- }
- return (error);
+ } else if (addr->sa_family != AF_INET) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EAFNOSUPPORT);
+ error = EAFNOSUPPORT;
+ } else if (addr->sa_len != sizeof(struct sockaddr_in)) {
+ SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
+ error = EINVAL;
}
-#ifdef INET6
- if (addr->sa_family != AF_INET) {
- /* must be a v4 address! */
- SCTP_LTRACE_ERR_RET_PKT(m, inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EDESTADDRREQ);
+ if (error != 0) {
sctp_m_freem(m);
if (control) {
sctp_m_freem(control);
control = NULL;
}
- error = EDESTADDRREQ;
return (error);
}
-#endif /* INET6 */
connected_type:
/* now what about control */
if (control) {
diff --git a/sys/netinet/tcp_usrreq.c b/sys/netinet/tcp_usrreq.c
index c4cfb5ea199f..cbc36860bf32 100644
--- a/sys/netinet/tcp_usrreq.c
+++ b/sys/netinet/tcp_usrreq.c
@@ -321,14 +321,16 @@ tcp_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
struct sockaddr_in *sinp;
sinp = (struct sockaddr_in *)nam;
- if (nam->sa_len != sizeof (*sinp))
+ if (nam->sa_family != AF_INET)
+ return (EAFNOSUPPORT);
+ if (nam->sa_len != sizeof(*sinp))
return (EINVAL);
+
/*
* Must check for multicast addresses and disallow binding
* to them.
*/
- if (sinp->sin_family == AF_INET &&
- IN_MULTICAST(ntohl(sinp->sin_addr.s_addr)))
+ if (IN_MULTICAST(ntohl(sinp->sin_addr.s_addr)))
return (EAFNOSUPPORT);
TCPDEBUG0;
@@ -364,14 +366,16 @@ tcp6_usr_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
u_char vflagsav;
sin6 = (struct sockaddr_in6 *)nam;
- if (nam->sa_len != sizeof (*sin6))
+ if (nam->sa_family != AF_INET6)
+ return (EAFNOSUPPORT);
+ if (nam->sa_len != sizeof(*sin6))
return (EINVAL);
+
/*
* Must check for multicast addresses and disallow binding
* to them.
*/
- if (sin6->sin6_family == AF_INET6 &&
- IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
return (EAFNOSUPPORT);
TCPDEBUG0;
@@ -542,16 +546,17 @@ tcp_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
struct sockaddr_in *sinp;
sinp = (struct sockaddr_in *)nam;
+ if (nam->sa_family != AF_INET)
+ return (EAFNOSUPPORT);
if (nam->sa_len != sizeof (*sinp))
return (EINVAL);
+
/*
* Must disallow TCP ``connections'' to multicast addresses.
*/
- if (sinp->sin_family == AF_INET
- && IN_MULTICAST(ntohl(sinp->sin_addr.s_addr)))
+ if (IN_MULTICAST(ntohl(sinp->sin_addr.s_addr)))
return (EAFNOSUPPORT);
- if ((sinp->sin_family == AF_INET) &&
- (ntohl(sinp->sin_addr.s_addr) == INADDR_BROADCAST))
+ if (ntohl(sinp->sin_addr.s_addr) == INADDR_BROADCAST)
return (EACCES);
if ((error = prison_remote_ip4(td->td_ucred, &sinp->sin_addr)) != 0)
return (error);
@@ -606,13 +611,15 @@ tcp6_usr_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
TCPDEBUG0;
sin6 = (struct sockaddr_in6 *)nam;
+ if (nam->sa_family != AF_INET6)
+ return (EAFNOSUPPORT);
if (nam->sa_len != sizeof (*sin6))
return (EINVAL);
+
/*
* Must disallow TCP ``connections'' to multicast addresses.
*/
- if (sin6->sin6_family == AF_INET6
- && IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
+ if (IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr))
return (EAFNOSUPPORT);
inp = sotoinpcb(so);
diff --git a/sys/netinet/udp_usrreq.c b/sys/netinet/udp_usrreq.c
index c2ad9381850e..16ae0a89bb15 100644
--- a/sys/netinet/udp_usrreq.c
+++ b/sys/netinet/udp_usrreq.c
@@ -1626,6 +1626,12 @@ udp_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp_bind: inp == NULL"));
+
+ if (nam->sa_family != AF_INET)
+ return (EAFNOSUPPORT);
+ if (nam->sa_len != sizeof(struct sockaddr_in))
+ return (EINVAL);
+
INP_WLOCK(inp);
INP_HASH_WLOCK(pcbinfo);
error = in_pcbbind(inp, nam, td->td_ucred);
@@ -1666,12 +1672,18 @@ udp_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
pcbinfo = udp_get_inpcbinfo(so->so_proto->pr_protocol);
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp_connect: inp == NULL"));
+
+ sin = (struct sockaddr_in *)nam;
+ if (sin->sin_family != AF_INET)
+ return (EAFNOSUPPORT);
+ if (sin->sin_len != sizeof(*sin))
+ return (EINVAL);
+
INP_WLOCK(inp);
if (inp->inp_faddr.s_addr != INADDR_ANY) {
INP_WUNLOCK(inp);
return (EISCONN);
}
- sin = (struct sockaddr_in *)nam;
error = prison_remote_ip4(td->td_ucred, &sin->sin_addr);
if (error != 0) {
INP_WUNLOCK(inp);
@@ -1741,9 +1753,23 @@ udp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
struct mbuf *control, struct thread *td)
{
struct inpcb *inp;
+ int error;
inp = sotoinpcb(so);
KASSERT(inp != NULL, ("udp_send: inp == NULL"));
+
+ if (addr != NULL) {
+ error = 0;
+ if (addr->sa_family != AF_INET)
+ error = EAFNOSUPPORT;
+ else if (addr->sa_len != sizeof(struct sockaddr_in))
+ error = EINVAL;
+ if (__predict_false(error != 0)) {
+ m_freem(control);
+ m_freem(m);
+ return (error);
+ }
+ }
return (udp_output(inp, m, addr, control, td, flags));
}
#endif /* INET */