Message ID | 20220927002544.3381205-1-kafai@fb.com (mailing list archive) |
---|---|
State | Accepted |
Commit | 5456262d2baa43c38e0c770543d5a31b0942f41c |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | [net-next] net: Fix incorrect address comparison when searching for a bind2 bucket | expand |
On Mon, Sep 26, 2022 at 5:25 PM Martin KaFai Lau <kafai@fb.com> wrote: > > 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. I do not see the KMSAN report, is it missing from this changelog ? Thanks. > > 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. > > Cc: Joanne Koong <joannelkoong@gmail.com> > Cc: Alexander Potapenko <glider@google.com> > Fixes: 28044fc1d495 ("net: Add a bhash2 table hashed by port and address") > Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org> > --- > include/net/inet_hashtables.h | 3 +++ > net/ipv4/inet_hashtables.c | 10 ++++++++++ > 2 files changed, 13 insertions(+) > > diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h > index 9121ccab1fa1..3af1e927247d 100644 > --- a/include/net/inet_hashtables.h > +++ b/include/net/inet_hashtables.h > @@ -95,6 +95,9 @@ struct inet_bind2_bucket { > possible_net_t ib_net; > int l3mdev; > unsigned short port; > +#if IS_ENABLED(CONFIG_IPV6) > + unsigned short family; > +#endif > union { > #if IS_ENABLED(CONFIG_IPV6) > struct in6_addr v6_rcv_saddr; > diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c > index 74e64aad5114..49db8c597eea 100644 > --- a/net/ipv4/inet_hashtables.c > +++ b/net/ipv4/inet_hashtables.c > @@ -109,6 +109,7 @@ static void inet_bind2_bucket_init(struct inet_bind2_bucket *tb, > tb->l3mdev = l3mdev; > tb->port = port; > #if IS_ENABLED(CONFIG_IPV6) > + tb->family = sk->sk_family; > if (sk->sk_family == AF_INET6) > tb->v6_rcv_saddr = sk->sk_v6_rcv_saddr; > else > @@ -146,6 +147,9 @@ static bool inet_bind2_bucket_addr_match(const struct inet_bind2_bucket *tb2, > const struct sock *sk) > { > #if IS_ENABLED(CONFIG_IPV6) > + if (sk->sk_family != tb2->family) > + return false; > + > if (sk->sk_family == AF_INET6) > return ipv6_addr_equal(&tb2->v6_rcv_saddr, > &sk->sk_v6_rcv_saddr); > @@ -791,6 +795,9 @@ static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb, > int l3mdev, const struct sock *sk) > { > #if IS_ENABLED(CONFIG_IPV6) > + if (sk->sk_family != tb->family) > + return false; > + > if (sk->sk_family == AF_INET6) > return net_eq(ib2_net(tb), net) && tb->port == port && > tb->l3mdev == l3mdev && > @@ -807,6 +814,9 @@ bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const > #if IS_ENABLED(CONFIG_IPV6) > struct in6_addr addr_any = {}; > > + if (sk->sk_family != tb->family) > + return false; > + > if (sk->sk_family == AF_INET6) > return net_eq(ib2_net(tb), net) && tb->port == port && > tb->l3mdev == l3mdev && > -- > 2.30.2 >
On 9/27/22 8:49 PM, Eric Dumazet wrote: > On Mon, Sep 26, 2022 at 5:25 PM Martin KaFai Lau <kafai@fb.com> wrote: >> >> 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. > > I do not see the KMSAN report, is it missing from this changelog ? My bad. Forgot to paste the link in the commit message. It is here: https://lore.kernel.org/netdev/CAG_fn=Ud3zSW7AZWXc+asfMhZVL5ETnvuY44Pmyv4NPv-ijN-A@mail.gmail.com/
On Tue, Sep 27, 2022 at 9:46 PM Martin KaFai Lau <martin.lau@linux.dev> wrote: > > On 9/27/22 8:49 PM, Eric Dumazet wrote: > > On Mon, Sep 26, 2022 at 5:25 PM Martin KaFai Lau <kafai@fb.com> wrote: > >> > >> 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. > > > > I do not see the KMSAN report, is it missing from this changelog ? > > My bad. Forgot to paste the link in the commit message. It is here: > > https://lore.kernel.org/netdev/CAG_fn=Ud3zSW7AZWXc+asfMhZVL5ETnvuY44Pmyv4NPv-ijN-A@mail.gmail.com/ I see, thanks. Reviewed-by: Eric Dumazet <edumazet@google.com>
On Wed, Sep 28, 2022 at 7:07 AM Eric Dumazet <edumazet@google.com> wrote: > > On Tue, Sep 27, 2022 at 9:46 PM Martin KaFai Lau <martin.lau@linux.dev> wrote: > > > > On 9/27/22 8:49 PM, Eric Dumazet wrote: > > > On Mon, Sep 26, 2022 at 5:25 PM Martin KaFai Lau <kafai@fb.com> wrote: > > >> > > >> 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. > > > > > > I do not see the KMSAN report, is it missing from this changelog ? > > > > My bad. Forgot to paste the link in the commit message. It is here: > > > > https://lore.kernel.org/netdev/CAG_fn=Ud3zSW7AZWXc+asfMhZVL5ETnvuY44Pmyv4NPv-ijN-A@mail.gmail.com/ > > I see, thanks. > > Reviewed-by: Eric Dumazet <edumazet@google.com> Tested-by: Alexander Potapenko <glider@google.com> Thanks!
Hello: This patch was applied to netdev/net-next.git (master) by Jakub Kicinski <kuba@kernel.org>: On Mon, 26 Sep 2022 17:25:44 -0700 you wrote: > 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. > > [...] Here is the summary with links: - [net-next] net: Fix incorrect address comparison when searching for a bind2 bucket https://git.kernel.org/netdev/net-next/c/5456262d2baa You are awesome, thank you!
On Mon, Sep 26, 2022 at 05:25:44PM -0700, Martin KaFai Lau wrote: > 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, Andrei > > Cc: Joanne Koong <joannelkoong@gmail.com> > Cc: Alexander Potapenko <glider@google.com> > Fixes: 28044fc1d495 ("net: Add a bhash2 table hashed by port and address") > Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org> > --- > include/net/inet_hashtables.h | 3 +++ > net/ipv4/inet_hashtables.c | 10 ++++++++++ > 2 files changed, 13 insertions(+) > > diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h > index 9121ccab1fa1..3af1e927247d 100644 > --- a/include/net/inet_hashtables.h > +++ b/include/net/inet_hashtables.h > @@ -95,6 +95,9 @@ struct inet_bind2_bucket { > possible_net_t ib_net; > int l3mdev; > unsigned short port; > +#if IS_ENABLED(CONFIG_IPV6) > + unsigned short family; > +#endif > union { > #if IS_ENABLED(CONFIG_IPV6) > struct in6_addr v6_rcv_saddr; > diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c > index 74e64aad5114..49db8c597eea 100644 > --- a/net/ipv4/inet_hashtables.c > +++ b/net/ipv4/inet_hashtables.c > @@ -109,6 +109,7 @@ static void inet_bind2_bucket_init(struct inet_bind2_bucket *tb, > tb->l3mdev = l3mdev; > tb->port = port; > #if IS_ENABLED(CONFIG_IPV6) > + tb->family = sk->sk_family; > if (sk->sk_family == AF_INET6) > tb->v6_rcv_saddr = sk->sk_v6_rcv_saddr; > else > @@ -146,6 +147,9 @@ static bool inet_bind2_bucket_addr_match(const struct inet_bind2_bucket *tb2, > const struct sock *sk) > { > #if IS_ENABLED(CONFIG_IPV6) > + if (sk->sk_family != tb2->family) > + return false; > + > if (sk->sk_family == AF_INET6) > return ipv6_addr_equal(&tb2->v6_rcv_saddr, > &sk->sk_v6_rcv_saddr); > @@ -791,6 +795,9 @@ static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb, > int l3mdev, const struct sock *sk) > { > #if IS_ENABLED(CONFIG_IPV6) > + if (sk->sk_family != tb->family) > + return false; > + > if (sk->sk_family == AF_INET6) > return net_eq(ib2_net(tb), net) && tb->port == port && > tb->l3mdev == l3mdev && > @@ -807,6 +814,9 @@ bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const > #if IS_ENABLED(CONFIG_IPV6) > struct in6_addr addr_any = {}; > > + if (sk->sk_family != tb->family) > + return false; > + > if (sk->sk_family == AF_INET6) > return net_eq(ib2_net(tb), net) && tb->port == port && > tb->l3mdev == l3mdev && > -- > 2.30.2 >
From: Andrei Vagin <avagin@gmail.com> Date: Fri, 8 Sep 2023 14:54:12 -0700 > On Mon, Sep 26, 2022 at 05:25:44PM -0700, Martin KaFai Lau wrote: > > 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 >>> 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<---
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 9121ccab1fa1..3af1e927247d 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -95,6 +95,9 @@ struct inet_bind2_bucket { possible_net_t ib_net; int l3mdev; unsigned short port; +#if IS_ENABLED(CONFIG_IPV6) + unsigned short family; +#endif union { #if IS_ENABLED(CONFIG_IPV6) struct in6_addr v6_rcv_saddr; diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 74e64aad5114..49db8c597eea 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -109,6 +109,7 @@ static void inet_bind2_bucket_init(struct inet_bind2_bucket *tb, tb->l3mdev = l3mdev; tb->port = port; #if IS_ENABLED(CONFIG_IPV6) + tb->family = sk->sk_family; if (sk->sk_family == AF_INET6) tb->v6_rcv_saddr = sk->sk_v6_rcv_saddr; else @@ -146,6 +147,9 @@ static bool inet_bind2_bucket_addr_match(const struct inet_bind2_bucket *tb2, const struct sock *sk) { #if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family != tb2->family) + return false; + if (sk->sk_family == AF_INET6) return ipv6_addr_equal(&tb2->v6_rcv_saddr, &sk->sk_v6_rcv_saddr); @@ -791,6 +795,9 @@ static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb, int l3mdev, const struct sock *sk) { #if IS_ENABLED(CONFIG_IPV6) + if (sk->sk_family != tb->family) + return false; + if (sk->sk_family == AF_INET6) return net_eq(ib2_net(tb), net) && tb->port == port && tb->l3mdev == l3mdev && @@ -807,6 +814,9 @@ bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const #if IS_ENABLED(CONFIG_IPV6) struct in6_addr addr_any = {}; + if (sk->sk_family != tb->family) + return false; + if (sk->sk_family == AF_INET6) return net_eq(ib2_net(tb), net) && tb->port == port && tb->l3mdev == l3mdev &&