⬢ DragonFlyBSD Kernel Audit
DF-3389 / fix.diff
← back to finding ↓ download raw
--- a/sys/netinet6/ip6_var.h
+++ b/sys/netinet6/ip6_var.h
@@ -268,7 +268,11 @@
 	u_int16_t	ip6a_bruid;	/* BR unique identifier */
 
 	/* ip6.ip6_dst */
-	struct in6_ifaddr *ip6a_dstia6;	/* my ifaddr that matches ip6_dst */
+	/* Snapshot of ia6_flags taken at ip6_input() time: the address may
+	 * be deleted (and the in6_ifaddr freed) while the packet is still
+	 * queued for the deferred transport-layer handler, so storing the
+	 * pointer itself would be a use-after-free read. */
+	u_int32_t	ip6a_dstia6_flags; /* ia6_flags of ifaddr matching ip6_dst */
 
 	/* rtalert */
 	u_int16_t ip6a_rtalert;		/* rtalert option value */
@@ -340,7 +344,7 @@
 
 void	ip6_init (void);
 void	ip6intr (void);
-struct in6_ifaddr *ip6_getdstifaddr (struct mbuf *);
+u_int32_t ip6_getdstifaddr_flags (struct mbuf *);
 void	ip6_freepcbopts (struct ip6_pktopts *);
 void	ip6_freemoptions (struct ip6_moptions *);
 int	ip6_unknown_opt (u_int8_t *, struct mbuf *, int);
--- a/sys/netinet6/ip6_input.c
+++ b/sys/netinet6/ip6_input.c
@@ -528,7 +528,7 @@
 	 * note that we are unable to record it, if the address is not listed
 	 * as our interface address (e.g. multicast addresses and such).
 	 */
-	if (deliverifp && !ip6_getdstifaddr(m)) {
+	if (deliverifp && ip6_findaux(m) == NULL) {
 		struct in6_ifaddr *ia6;
 
 		ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst);
@@ -800,20 +800,26 @@
 
 	n = ip6_addaux(m);
 	if (n)
-		n->ip6a_dstia6 = ia6;
+		n->ip6a_dstia6_flags = ia6->ia6_flags;
 	return n;	/* NULL if failed to set */
 }
 
-struct in6_ifaddr *
-ip6_getdstifaddr(struct mbuf *m)
+/*
+ * Return the ia6_flags snapshot of the ifaddr that matched ip6_dst at
+ * ip6_input() time.  The pointer itself is deliberately not kept: the
+ * address can be purged while the packet sits in the netisr queue
+ * waiting for the deferred transport handler (DF-3389).
+ */
+u_int32_t
+ip6_getdstifaddr_flags(struct mbuf *m)
 {
 	struct ip6aux *n;
 
 	n = ip6_findaux(m);
 	if (n)
-		return n->ip6a_dstia6;
+		return n->ip6a_dstia6_flags;
 	else
-		return NULL;
+		return 0;
 }
 
 /*
--- a/sys/netinet/tcp_input.c
+++ b/sys/netinet/tcp_input.c
@@ -613,7 +613,6 @@
 tcp6_input(struct mbuf **mp, int *offp, int proto)
 {
 	struct mbuf *m = *mp;
-	struct in6_ifaddr *ia6;
 
 	IP6_EXTHDR_CHECK(m, *offp, sizeof(struct tcphdr), IPPROTO_DONE);
 
@@ -621,8 +620,7 @@
 	 * draft-itojun-ipv6-tcp-to-anycast
 	 * better place to put this in?
 	 */
-	ia6 = ip6_getdstifaddr(m);
-	if (ia6 && (ia6->ia6_flags & IN6_IFF_ANYCAST)) {
+	if (ip6_getdstifaddr_flags(m) & IN6_IFF_ANYCAST) {
 		icmp6_error(m, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR,
 			    offsetof(struct ip6_hdr, ip6_dst));
 		return (IPPROTO_DONE);
@@ -1065,15 +1063,11 @@
 		 * handling - worse, they are not exactly the same.
 		 * I believe 5.5.4 is the best one, so we follow 5.5.4.
 		 */
-		if (isipv6 && !ip6_use_deprecated) {
-			struct in6_ifaddr *ia6;
-
-			if ((ia6 = ip6_getdstifaddr(m)) &&
-			    (ia6->ia6_flags & IN6_IFF_DEPRECATED)) {
-				tp = NULL;
-				rstreason = BANDLIM_RST_OPENPORT;
-				goto dropwithreset;
-			}
+		if (isipv6 && !ip6_use_deprecated &&
+		    (ip6_getdstifaddr_flags(m) & IN6_IFF_DEPRECATED)) {
+			tp = NULL;
+			rstreason = BANDLIM_RST_OPENPORT;
+			goto dropwithreset;
 		}
 #endif
 		/*