Re: [PATCH net-next] net: Fix incorrect address comparison when searching for a bind2 bucket
From: Kuniyuki Iwashima <hidden>
Date: 2023-09-09 07:05:43
Subsystem:
networking [general], networking [tcp], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Neal Cardwell, Linus Torvalds
From: Andrei Vagin <redacted> Date: Fri, 8 Sep 2023 14:54:12 -0700
On Mon, Sep 26, 2022 at 05:25:44PM -0700, Martin KaFai Lau wrote:quoted
From: Martin KaFai Lau <martin.lau@kernel.org> The v6_rcv_saddr and rcv_saddr are inside a union in the 'struct inet_bind2_bucket'. When searching a bucket by following the bhash2 hashtable chain, eg. inet_bind2_bucket_match, it is only using the sk->sk_family and there is no way to check if the inet_bind2_bucket has a v6 or v4 address in the union. This leads to an uninit-value KMSAN report in [0] and also potentially incorrect matches. This patch fixes it by adding a family member to the inet_bind2_bucket and then tests 'sk->sk_family != tb->family' before matching the sk's address to the tb's address.It seems this patch doesn't handle v4mapped addresses properly. One of gVisor test started failing with this change: socket(AF_INET6, SOCK_STREAM, IPPROTO_IP) = 3 bind(3, {sa_family=AF_INET6, sin6_port=htons(0), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::ffff:0.0.0.0", &sin6_addr), sin6_scope_id=0}, 28) = 0 getsockname(3, {sa_family=AF_INET6, sin6_port=htons(33789), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::ffff:0.0.0.0", &sin6_addr), sin6_scope_id=0}, [28]) = 0 socket(AF_INET6, SOCK_STREAM, IPPROTO_IP) = 4 bind(4, {sa_family=AF_INET6, sin6_port=htons(33789), sin6_flowinfo=htonl(0), inet_pton(AF_INET6, "::1", &sin6_addr), sin6_scope_id=0}, 28) = 0 socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 5 bind(5, {sa_family=AF_INET, sin_port=htons(33789), sin_addr=inet_addr("127.0.0.1")}, 16) = 0 The test expects that the second bind returns EADDRINUSE.
Thanks for the report. inet_bind2_bucket_match_addr_any() forgot to take care of IPV6_ADDR_MAPPED inaddr_any case. This change fixes the regression. I'll post a patch after checking other two functions that the commit touched. ---8<---
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index 7876b7d703cb..6f2a8dba24fe 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c@@ -837,7 +837,9 @@ bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const if (sk->sk_family == AF_INET) return net_eq(ib2_net(tb), net) && tb->port == port && tb->l3mdev == l3mdev && - ipv6_addr_any(&tb->v6_rcv_saddr); + (ipv6_addr_any(&tb->v6_rcv_saddr) || + (ipv6_addr_type(&tb->v6_rcv_saddr) == IPV6_ADDR_MAPPED && + tb->v6_rcv_saddr.s6_addr32[3] == 0)); return false; } ---8<--- ---8<---
[root@localhost ~]# python3
quoted
quoted
from socket import * s1 = socket(AF_INET6, SOCK_STREAM) s1.bind(('::ffff:0.0.0.0', 0)) port = s1.getsockname()[1] s2 = socket(AF_INET, SOCK_STREAM) s2.bind(('127.0.0.1', port))
Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: [Errno 98] Address already in use ---8<---