diff mbox series

[net-next] net: Fix incorrect address comparison when searching for a bind2 bucket

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

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 994 this patch: 994
netdev/cc_maintainers warning 2 maintainers not CCed: dsahern@kernel.org yoshfuji@linux-ipv6.org
netdev/build_clang success Errors and warnings before: 118 this patch: 118
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 999 this patch: 999
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 43 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Martin KaFai Lau Sept. 27, 2022, 12:25 a.m. UTC
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.

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(+)

Comments

Eric Dumazet Sept. 28, 2022, 3:49 a.m. UTC | #1
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
>
Martin KaFai Lau Sept. 28, 2022, 4:46 a.m. UTC | #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/
Eric Dumazet Sept. 28, 2022, 5:07 a.m. UTC | #3
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>
Alexander Potapenko Sept. 28, 2022, 11:15 a.m. UTC | #4
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!
patchwork-bot+netdevbpf@kernel.org Sept. 29, 2022, 2:20 a.m. UTC | #5
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!
Andrei Vagin Sept. 8, 2023, 9:54 p.m. UTC | #6
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
>
Kuniyuki Iwashima Sept. 9, 2023, 7:05 a.m. UTC | #7
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 mbox series

Patch

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 &&