Module Name: src
Committed By: ozaki-r
Date: Mon Oct 31 04:16:25 UTC 2016
Modified Files:
src/sys/netinet6: icmp6.c in6_pcb.c in6_src.c ip6_var.h nd6_nbr.c
raw_ip6.c udp6_output.c
Log Message:
Fix race condition of in6_selectsrc
in6_selectsrc returned a pointer to in6_addr that wan't guaranteed to be
safe by pserialize (or psref), which was racy. Let callers pass a pointer
to in6_addr and in6_selectsrc copy a result to it inside pserialize
critical sections.
To generate a diff of this commit:
cvs rdiff -u -r1.199 -r1.200 src/sys/netinet6/icmp6.c
cvs rdiff -u -r1.150 -r1.151 src/sys/netinet6/in6_pcb.c
cvs rdiff -u -r1.71 -r1.72 src/sys/netinet6/in6_src.c
cvs rdiff -u -r1.68 -r1.69 src/sys/netinet6/ip6_var.h
cvs rdiff -u -r1.128 -r1.129 src/sys/netinet6/nd6_nbr.c
cvs rdiff -u -r1.151 -r1.152 src/sys/netinet6/raw_ip6.c
cvs rdiff -u -r1.53 -r1.54 src/sys/netinet6/udp6_output.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/netinet6/icmp6.c
diff -u src/sys/netinet6/icmp6.c:1.199 src/sys/netinet6/icmp6.c:1.200
--- src/sys/netinet6/icmp6.c:1.199 Tue Oct 25 02:45:10 2016
+++ src/sys/netinet6/icmp6.c Mon Oct 31 04:16:25 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: icmp6.c,v 1.199 2016/10/25 02:45:10 ozaki-r Exp $ */
+/* $NetBSD: icmp6.c,v 1.200 2016/10/31 04:16:25 ozaki-r Exp $ */
/* $KAME: icmp6.c,v 1.217 2001/06/20 15:03:29 jinmei Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: icmp6.c,v 1.199 2016/10/25 02:45:10 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: icmp6.c,v 1.200 2016/10/31 04:16:25 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -1999,9 +1999,9 @@ icmp6_reflect(struct mbuf *m, size_t off
int type, code;
struct ifnet *outif = NULL;
struct in6_addr origdst;
- const struct in6_addr *src = NULL;
struct ifnet *rcvif;
int s;
+ bool ip6_src_filled = false;
/* too short to reflect */
if (off < sizeof(struct ip6_hdr)) {
@@ -2069,8 +2069,10 @@ icmp6_reflect(struct mbuf *m, size_t off
;
else if ((ip6a = ip6_getdstifaddr(m)) != NULL) {
if ((ip6a->ip6a_flags &
- (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0)
- src = &ip6a->ip6a_src;
+ (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) {
+ ip6->ip6_src = ip6a->ip6a_src;
+ ip6_src_filled = true;
+ }
} else {
union {
struct sockaddr_in6 sin6;
@@ -2087,13 +2089,15 @@ icmp6_reflect(struct mbuf *m, size_t off
if (ifa != NULL) {
ia = ifatoia6(ifa);
if ((ia->ia6_flags &
- (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0)
- src = &ia->ia_addr.sin6_addr;
+ (IN6_IFF_ANYCAST|IN6_IFF_NOTREADY)) == 0) {
+ ip6->ip6_src = ia->ia_addr.sin6_addr;
+ ip6_src_filled = true;
+ }
}
pserialize_read_exit(_s);
}
- if (src == NULL) {
+ if (!ip6_src_filled) {
int e;
struct sockaddr_in6 sin6;
struct route ro;
@@ -2107,9 +2111,10 @@ icmp6_reflect(struct mbuf *m, size_t off
sockaddr_in6_init(&sin6, &ip6->ip6_dst, 0, 0, 0);
memset(&ro, 0, sizeof(ro));
- src = in6_selectsrc(&sin6, NULL, NULL, &ro, NULL, NULL, NULL, &e);
+ e = in6_selectsrc(&sin6, NULL, NULL, &ro, NULL, NULL, NULL,
+ &ip6->ip6_src);
rtcache_free(&ro);
- if (src == NULL) {
+ if (e != 0) {
nd6log(LOG_DEBUG,
"source can't be determined: "
"dst=%s, error=%d\n",
@@ -2118,7 +2123,6 @@ icmp6_reflect(struct mbuf *m, size_t off
}
}
- ip6->ip6_src = *src;
ip6->ip6_flow = 0;
ip6->ip6_vfc &= ~IPV6_VERSION_MASK;
ip6->ip6_vfc |= IPV6_VERSION;
Index: src/sys/netinet6/in6_pcb.c
diff -u src/sys/netinet6/in6_pcb.c:1.150 src/sys/netinet6/in6_pcb.c:1.151
--- src/sys/netinet6/in6_pcb.c:1.150 Thu Sep 29 12:19:47 2016
+++ src/sys/netinet6/in6_pcb.c Mon Oct 31 04:16:25 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: in6_pcb.c,v 1.150 2016/09/29 12:19:47 roy Exp $ */
+/* $NetBSD: in6_pcb.c,v 1.151 2016/10/31 04:16:25 ozaki-r Exp $ */
/* $KAME: in6_pcb.c,v 1.84 2001/02/08 18:02:08 itojun Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_pcb.c,v 1.150 2016/09/29 12:19:47 roy Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_pcb.c,v 1.151 2016/10/31 04:16:25 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -444,6 +444,7 @@ in6_pcbconnect(void *v, struct sockaddr_
{
struct in6pcb *in6p = v;
struct in6_addr *in6a = NULL;
+ struct in6_addr ia6;
struct ifnet *ifp = NULL; /* outgoing interface */
int error = 0;
int scope_ambiguous = 0;
@@ -530,10 +531,9 @@ in6_pcbconnect(void *v, struct sockaddr_
* with the address specified by setsockopt(IPV6_PKTINFO).
* Is it the intended behavior?
*/
- in6a = in6_selectsrc(sin6, in6p->in6p_outputopts,
- in6p->in6p_moptions,
- &in6p->in6p_route,
- &in6p->in6p_laddr, &ifp, &psref, &error);
+ error = in6_selectsrc(sin6, in6p->in6p_outputopts,
+ in6p->in6p_moptions, &in6p->in6p_route, &in6p->in6p_laddr,
+ &ifp, &psref, &ia6);
if (ifp && scope_ambiguous &&
(error = in6_setscope(&sin6->sin6_addr, ifp, NULL)) != 0) {
if_put(ifp, &psref);
@@ -541,13 +541,14 @@ in6_pcbconnect(void *v, struct sockaddr_
return(error);
}
- if (in6a == NULL) {
+ if (error != 0) {
if_put(ifp, &psref);
curlwp_bindx(bound);
if (error == 0)
error = EADDRNOTAVAIL;
return (error);
}
+ in6a = &ia6;
}
if (ifp != NULL) {
Index: src/sys/netinet6/in6_src.c
diff -u src/sys/netinet6/in6_src.c:1.71 src/sys/netinet6/in6_src.c:1.72
--- src/sys/netinet6/in6_src.c:1.71 Mon Oct 31 02:50:31 2016
+++ src/sys/netinet6/in6_src.c Mon Oct 31 04:16:25 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: in6_src.c,v 1.71 2016/10/31 02:50:31 ozaki-r Exp $ */
+/* $NetBSD: in6_src.c,v 1.72 2016/10/31 04:16:25 ozaki-r Exp $ */
/* $KAME: in6_src.c,v 1.159 2005/10/19 01:40:32 t-momose Exp $ */
/*
@@ -66,7 +66,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: in6_src.c,v 1.71 2016/10/31 02:50:31 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: in6_src.c,v 1.72 2016/10/31 04:16:25 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -171,10 +171,10 @@ static struct in6_addrpolicy *match_addr
#define BREAK(r) goto out
#endif
-struct in6_addr *
+int
in6_selectsrc(struct sockaddr_in6 *dstsock, struct ip6_pktopts *opts,
struct ip6_moptions *mopts, struct route *ro, struct in6_addr *laddr,
- struct ifnet **ifpp, struct psref *psref, int *errorp)
+ struct ifnet **ifpp, struct psref *psref, struct in6_addr *ret_ia6)
{
struct in6_addr dst;
struct ifnet *ifp = NULL;
@@ -189,7 +189,6 @@ in6_selectsrc(struct sockaddr_in6 *dstso
u_int8_t ip6po_usecoa = 0;
#endif /* MIP6 && NMIP > 0 */
struct psref local_psref;
- struct in6_addr *ret_ia = NULL;
int bound = curlwp_bind();
#define PSREF (psref == NULL) ? &local_psref : psref
int s;
@@ -198,7 +197,6 @@ in6_selectsrc(struct sockaddr_in6 *dstso
(ifpp == NULL && psref == NULL));
dst = dstsock->sin6_addr; /* make a copy for local operation */
- *errorp = 0;
if (ifpp)
*ifpp = NULL;
@@ -238,8 +236,8 @@ in6_selectsrc(struct sockaddr_in6 *dstso
srcsock.sin6_len = sizeof(srcsock);
srcsock.sin6_addr = pi->ipi6_addr;
if (ifp) {
- *errorp = in6_setscope(&srcsock.sin6_addr, ifp, NULL);
- if (*errorp != 0)
+ error = in6_setscope(&srcsock.sin6_addr, ifp, NULL);
+ if (error != 0)
goto exit;
}
@@ -249,15 +247,14 @@ in6_selectsrc(struct sockaddr_in6 *dstso
ia6->ia6_flags &
(IN6_IFF_ANYCAST | IN6_IFF_NOTREADY)) {
pserialize_read_exit(_s);
- *errorp = EADDRNOTAVAIL;
+ error = EADDRNOTAVAIL;
goto exit;
}
pi->ipi6_addr = srcsock.sin6_addr; /* XXX: this overrides pi */
if (ifpp)
*ifpp = ifp;
- ret_ia = &ia6->ia_addr.sin6_addr;
+ *ret_ia6 = ia6->ia_addr.sin6_addr;
pserialize_read_exit(_s);
- /* XXX don't return pointer */
goto exit;
}
@@ -267,7 +264,7 @@ in6_selectsrc(struct sockaddr_in6 *dstso
* though it would eventually cause an error.
*/
if (laddr && !IN6_IS_ADDR_UNSPECIFIED(laddr)) {
- ret_ia = laddr;
+ *ret_ia6 = *laddr;
goto exit;
}
@@ -275,10 +272,8 @@ in6_selectsrc(struct sockaddr_in6 *dstso
* The outgoing interface is crucial in the general selection procedure
* below. If it is not known at this point, we fail.
*/
- if (ifp == NULL) {
- *errorp = error;
+ if (ifp == NULL)
goto exit;
- }
/*
* If the address is not yet determined, choose the best one based on
@@ -297,8 +292,8 @@ in6_selectsrc(struct sockaddr_in6 *dstso
}
#endif /* MIP6 && NMIP > 0 */
- *errorp = in6_setscope(&dst, ifp, &odstzone);
- if (*errorp != 0)
+ error = in6_setscope(&dst, ifp, &odstzone);
+ if (error != 0)
goto exit;
s = pserialize_read_enter();
@@ -560,19 +555,20 @@ in6_selectsrc(struct sockaddr_in6 *dstso
out:
break;
}
- pserialize_read_exit(s);
if ((ia = ia_best) == NULL) {
- *errorp = EADDRNOTAVAIL;
+ pserialize_read_exit(s);
+ error = EADDRNOTAVAIL;
goto exit;
}
- ret_ia = &ia->ia_addr.sin6_addr;
+ *ret_ia6 = ia->ia_addr.sin6_addr;
+ pserialize_read_exit(s);
exit:
if (ifpp == NULL)
if_put(ifp, PSREF);
curlwp_bindx(bound);
- return ret_ia;
+ return error;
#undef PSREF
}
#undef REPLACE
Index: src/sys/netinet6/ip6_var.h
diff -u src/sys/netinet6/ip6_var.h:1.68 src/sys/netinet6/ip6_var.h:1.69
--- src/sys/netinet6/ip6_var.h:1.68 Tue Aug 23 09:59:20 2016
+++ src/sys/netinet6/ip6_var.h Mon Oct 31 04:16:25 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: ip6_var.h,v 1.68 2016/08/23 09:59:20 knakahara Exp $ */
+/* $NetBSD: ip6_var.h,v 1.69 2016/10/31 04:16:25 ozaki-r Exp $ */
/* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */
/*
@@ -396,9 +396,9 @@ int none_input(struct mbuf **, int *, in
struct route;
-struct in6_addr *in6_selectsrc(struct sockaddr_in6 *,
- struct ip6_pktopts *, struct ip6_moptions *, struct route *,
- struct in6_addr *, struct ifnet **, struct psref *, int *);
+int in6_selectsrc(struct sockaddr_in6 *, struct ip6_pktopts *,
+ struct ip6_moptions *, struct route *, struct in6_addr *,
+ struct ifnet **, struct psref *, struct in6_addr *);
int in6_selectroute(struct sockaddr_in6 *, struct ip6_pktopts *,
struct ip6_moptions *, struct route *, struct ifnet **,
struct psref *, struct rtentry **, int);
Index: src/sys/netinet6/nd6_nbr.c
diff -u src/sys/netinet6/nd6_nbr.c:1.128 src/sys/netinet6/nd6_nbr.c:1.129
--- src/sys/netinet6/nd6_nbr.c:1.128 Tue Oct 18 07:30:31 2016
+++ src/sys/netinet6/nd6_nbr.c Mon Oct 31 04:16:25 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: nd6_nbr.c,v 1.128 2016/10/18 07:30:31 ozaki-r Exp $ */
+/* $NetBSD: nd6_nbr.c,v 1.129 2016/10/31 04:16:25 ozaki-r Exp $ */
/* $KAME: nd6_nbr.c,v 1.61 2001/02/10 16:06:14 jinmei Exp $ */
/*
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.128 2016/10/18 07:30:31 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: nd6_nbr.c,v 1.129 2016/10/31 04:16:25 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -469,15 +469,16 @@ nd6_ns_output(struct ifnet *ifp, const s
sockaddr_in6_init(&dst_sa, &ip6->ip6_dst, 0, 0, 0);
- src = in6_selectsrc(&dst_sa, NULL,
- NULL, &ro, NULL, NULL, NULL, &error);
- if (src == NULL) {
+ error = in6_selectsrc(&dst_sa, NULL,
+ NULL, &ro, NULL, NULL, NULL, &src_in);
+ if (error != 0) {
nd6log(LOG_DEBUG, "source can't be "
"determined: dst=%s, error=%d\n",
ip6_sprintf(&dst_sa.sin6_addr), error);
pserialize_read_exit(s);
goto bad;
}
+ src = &src_in;
}
pserialize_read_exit(s);
} else {
@@ -894,7 +895,7 @@ nd6_na_output(
struct sockaddr dst;
struct sockaddr_in6 dst6;
} u;
- struct in6_addr *src, daddr6;
+ struct in6_addr daddr6;
int icmp6len, maxlen, error;
const void *mac;
struct route ro;
@@ -967,14 +968,14 @@ nd6_na_output(
/*
* Select a source whose scope is the same as that of the dest.
*/
- src = in6_selectsrc(satosin6(dst), NULL, NULL, &ro, NULL, NULL, NULL, &error);
- if (src == NULL) {
+ error = in6_selectsrc(satosin6(dst), NULL, NULL, &ro, NULL, NULL, NULL,
+ &ip6->ip6_src);
+ if (error != 0) {
nd6log(LOG_DEBUG, "source can't be "
"determined: dst=%s, error=%d\n",
ip6_sprintf(&satocsin6(dst)->sin6_addr), error);
goto bad;
}
- ip6->ip6_src = *src;
nd_na = (struct nd_neighbor_advert *)(ip6 + 1);
nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
nd_na->nd_na_code = 0;
Index: src/sys/netinet6/raw_ip6.c
diff -u src/sys/netinet6/raw_ip6.c:1.151 src/sys/netinet6/raw_ip6.c:1.152
--- src/sys/netinet6/raw_ip6.c:1.151 Thu Sep 29 12:19:47 2016
+++ src/sys/netinet6/raw_ip6.c Mon Oct 31 04:16:25 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: raw_ip6.c,v 1.151 2016/09/29 12:19:47 roy Exp $ */
+/* $NetBSD: raw_ip6.c,v 1.152 2016/10/31 04:16:25 ozaki-r Exp $ */
/* $KAME: raw_ip6.c,v 1.82 2001/07/23 18:57:56 jinmei Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: raw_ip6.c,v 1.151 2016/09/29 12:19:47 roy Exp $");
+__KERNEL_RCSID(0, "$NetBSD: raw_ip6.c,v 1.152 2016/10/31 04:16:25 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_ipsec.h"
@@ -386,7 +386,6 @@ rip6_output(struct mbuf *m, struct socke
struct ifnet *oifp = NULL;
int type, code; /* for ICMPv6 output statistics only */
int scope_ambiguous = 0;
- struct in6_addr *in6a;
int bound = curlwp_bind();
struct psref psref;
@@ -448,13 +447,10 @@ rip6_output(struct mbuf *m, struct socke
/*
* Source address selection.
*/
- if ((in6a = in6_selectsrc(dstsock, optp, in6p->in6p_moptions,
- &in6p->in6p_route, &in6p->in6p_laddr, &oifp, &psref, &error)) == 0) {
- if (error == 0)
- error = EADDRNOTAVAIL;
+ error = in6_selectsrc(dstsock, optp, in6p->in6p_moptions,
+ &in6p->in6p_route, &in6p->in6p_laddr, &oifp, &psref, &ip6->ip6_src);
+ if (error != 0)
goto bad;
- }
- ip6->ip6_src = *in6a;
if (oifp && scope_ambiguous) {
/*
@@ -725,7 +721,7 @@ rip6_connect(struct socket *so, struct s
{
struct in6pcb *in6p = sotoin6pcb(so);
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)nam;
- struct in6_addr *in6a = NULL;
+ struct in6_addr in6a;
struct ifnet *ifp = NULL;
int scope_ambiguous = 0;
int error = 0;
@@ -756,20 +752,17 @@ rip6_connect(struct socket *so, struct s
bound = curlwp_bind();
/* Source address selection. XXX: need pcblookup? */
- in6a = in6_selectsrc(addr, in6p->in6p_outputopts,
+ error = in6_selectsrc(addr, in6p->in6p_outputopts,
in6p->in6p_moptions, &in6p->in6p_route,
- &in6p->in6p_laddr, &ifp, &psref, &error);
- if (in6a == NULL) {
- if (error == 0)
- error = EADDRNOTAVAIL;
+ &in6p->in6p_laddr, &ifp, &psref, &in6a);
+ if (error != 0)
goto out;
- }
/* XXX: see above */
if (ifp && scope_ambiguous &&
(error = in6_setscope(&addr->sin6_addr, ifp, NULL)) != 0) {
goto out;
}
- in6p->in6p_laddr = *in6a;
+ in6p->in6p_laddr = in6a;
in6p->in6p_faddr = addr->sin6_addr;
soisconnected(so);
out:
Index: src/sys/netinet6/udp6_output.c
diff -u src/sys/netinet6/udp6_output.c:1.53 src/sys/netinet6/udp6_output.c:1.54
--- src/sys/netinet6/udp6_output.c:1.53 Mon Aug 1 03:15:31 2016
+++ src/sys/netinet6/udp6_output.c Mon Oct 31 04:16:25 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: udp6_output.c,v 1.53 2016/08/01 03:15:31 ozaki-r Exp $ */
+/* $NetBSD: udp6_output.c,v 1.54 2016/10/31 04:16:25 ozaki-r Exp $ */
/* $KAME: udp6_output.c,v 1.43 2001/10/15 09:19:52 itojun Exp $ */
/*
@@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: udp6_output.c,v 1.53 2016/08/01 03:15:31 ozaki-r Exp $");
+__KERNEL_RCSID(0, "$NetBSD: udp6_output.c,v 1.54 2016/10/31 04:16:25 ozaki-r Exp $");
#ifdef _KERNEL_OPT
#include "opt_inet.h"
@@ -120,7 +120,7 @@ udp6_output(struct in6pcb * const in6p,
u_int32_t plen = sizeof(struct udphdr) + ulen;
struct ip6_hdr *ip6;
struct udphdr *udp6;
- struct in6_addr *laddr, *faddr;
+ struct in6_addr _laddr, *laddr, *faddr;
struct in6_addr laddr_mapped; /* XXX ugly */
struct sockaddr_in6 *sin6 = NULL;
struct ifnet *oifp = NULL;
@@ -229,10 +229,11 @@ udp6_output(struct in6pcb * const in6p,
struct psref psref;
int bound = curlwp_bind();
- laddr = in6_selectsrc(sin6, optp,
+ error = in6_selectsrc(sin6, optp,
in6p->in6p_moptions,
&in6p->in6p_route,
- &in6p->in6p_laddr, &oifp, &psref, &error);
+ &in6p->in6p_laddr, &oifp, &psref, &_laddr);
+ /* XXX need error check? */
if (oifp && scope_ambiguous &&
(error = in6_setscope(&sin6->sin6_addr,
oifp, NULL))) {
@@ -242,6 +243,7 @@ udp6_output(struct in6pcb * const in6p,
}
if_put(oifp, &psref);
curlwp_bindx(bound);
+ laddr = &_laddr;
} else {
/*
* XXX: freebsd[34] does not have in_selectsrc, but