DF-3389 / fix.diff
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | --- 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 /* |