Commit dcc32f4f183a ("ipv6: weaken the v4mapped source check") removed the v4mapped source address check from the IPv6 input path and moved the TCP check to tcp_v6_conn_request(). The check is applied to SYN packets handled by tcp_v6_conn_request(). However, an ACK that passes SYN cookie validation is handled by cookie_v6_check() without passing through tcp_v6_conn_request(). cookie_v6_check() initializes the IPv6 address fields of the request socket, but not its IPv4 address fields. In the normal SYN cookie case, inet_csk_clone_lock() copies those uninitialized IPv4 address fields to the child socket. The child is an AF_INET6 socket, but its remote IPv6 address is v4mapped. sk_ehashfn() therefore uses the IPv4 address fields and TCP ports to compute the hash. The child is then inserted into ehash with that hash. tcp_v4_err() uses __inet_lookup_established(), which does not exclude AF_INET6 sockets. An ICMPv4 error can therefore find this child socket if its quoted IPv4 tuple matches and the interface and sequence checks pass. For testing, the PoC used a server helper to construct an ACK with a v4mapped source address that passed SYN cookie validation. Before accept(), a matching ICMPv4 Fragmentation Needed message passed the socket lookup and sequence checks in tcp_v4_err(). At that point sock_owned_by_user() returned false, so tcp_v4_err() called tcp_v4_mtu_reduced() directly. inet_csk_rebuild_route() then created an IPv4 rtable and cached it in the socket. When the socket next transmitted over IPv6, ipv4_dst_check() returned the cached rtable. ip6_xmit() then called ip6_dst_idev(), which interpreted the rtable as struct rt6_info and read rt6i_idev. KASAN reported a read of 8 bytes starting 32 bytes past the end of an rtable object whose size was 176 bytes. KASAN report: BUG: KASAN: slab-out-of-bounds in ip6_xmit+0x119/0xb10 Read of size 8 at addr ffff88810356a9d0 by task target-server/79 ... Call Trace: ... ip6_xmit+0x119/0xb10 ... inet6_csk_xmit+0x124/0x1d0 __tcp_transmit_skb+0xda0/0x1be0 ... tcp_write_xmit+0x985/0x2f80 __tcp_push_pending_frames+0x5a/0x150 tcp_sendmsg_locked+0xe7f/0x1b60 ... ... Allocated by task 0: ... dst_alloc+0x52/0x250 rt_dst_alloc+0x33/0x170 ip_route_output_key_hash_rcu+0x6df/0xe20 ip_route_output_flow+0xab/0x160 inet_csk_rebuild_route+0x2a8/0x300 inet_csk_update_pmtu+0x85/0xb0 tcp_v4_mtu_reduced+0x52/0x170 tcp_v4_err+0x651/0x710 icmp_unreach+0x1c7/0x370 icmp_rcv+0x466/0x5a0 ... The buggy address belongs to the object at ffff88810356a900 which belongs to the cache rtable of size 176 The buggy address is located 32 bytes to the right of allocated 176-byte region [ffff88810356a900, ffff88810356a9b0) Reject v4mapped source addresses in cookie_v6_check() before creating the child socket. As in tcp_v6_conn_request(), increment IPSTATS_MIB_INHDRERRORS and drop the packet. Fixes: dcc32f4f183a ("ipv6: weaken the v4mapped source check") Cc: stable@vger.kernel.org Assisted-by: LLM Signed-off-by: Hyunwoo Kim --- net/ipv6/syncookies.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index b581cb1ee2e8a..6c5bb4bbd349c 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -189,6 +189,12 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) !th->ack || th->rst) goto out; + if (ipv6_addr_v4mapped(&ipv6_hdr(skb)->saddr)) { + __IP6_INC_STATS(net, NULL, IPSTATS_MIB_INHDRERRORS); + SKB_DR_SET(reason, IP_INHDR); + goto out_drop; + } + if (cookie_bpf_ok(skb)) { req = cookie_bpf_check(sk, skb); } else { -- 2.43.0