Netdev List
 help / color / mirror / Atom feed
* [PATCH net] net/x25: fix use-after-free in x25_kill_by_neigh()
@ 2026-07-13 10:47 David Lee
  2026-07-14  6:42 ` Martin Schiller
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: David Lee @ 2026-07-13 10:47 UTC (permalink / raw)
  To: Martin Schiller
  Cc: David Lee, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman,
	Dominik 'Disconnect3d' Czarnota, linux-x25, netdev,
	linux-kernel, stable

x25_kill_by_neigh() walks the global X.25 socket list looking for sockets
attached to a terminating neighbour. x25_list_lock protects list membership
while the lookup is in progress, but it does not pin a socket's lifetime
after the lock is dropped.

The function currently drops x25_list_lock before calling lock_sock(s). A
concurrent close can run x25_release(), remove the same socket from
x25_list, and drop the last socket reference in that window. The neighbour
teardown path can then lock or inspect a freed struct sock/struct x25_sock.

Take sock_hold(s) while x25_list_lock still proves that the list entry is
live, then drop the temporary reference after the socket has been locked,
rechecked, and released. Recheck x25_sk(s)->neighbour after lock_sock(),
because another path may have disconnected the socket before this path
acquired the socket lock. Restart the list walk after each disconnect
because the list lock was dropped and the previous iterator state may no
longer be valid.

A QEMU/KASAN run against origin/master reproduced a slab-use-after-free in
x25_kill_by_neigh().

Fixes: 7781607938c8 ("net/x25: Fix null-ptr-deref caused by x25_disconnect")
Cc: stable@vger.kernel.org
Signed-off-by: David Lee <david.lee@trailofbits.com>
Assisted-by: Codex:gpt-5.5
---
Trail of Bits has a reproducer that triggers kernel panic demonstrating the bug, and can share it if needed.

net/x25/af_x25.c |    8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index c31d2af5dd22..8aae9273b7c1 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -1768,15 +1768,19 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
 {
 	struct sock *s;
 
+again:
 	write_lock_bh(&x25_list_lock);
 
 	sk_for_each(s, &x25_list) {
 		if (x25_sk(s)->neighbour == nb) {
+			sock_hold(s);
 			write_unlock_bh(&x25_list_lock);
 			lock_sock(s);
-			x25_disconnect(s, ENETUNREACH, 0, 0);
+			if (x25_sk(s)->neighbour == nb)
+				x25_disconnect(s, ENETUNREACH, 0, 0);
 			release_sock(s);
-			write_lock_bh(&x25_list_lock);
+			sock_put(s);
+			goto again;
 		}
 	}
 	write_unlock_bh(&x25_list_lock);

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH net] net/x25: fix use-after-free in x25_kill_by_neigh()
  2026-07-13 10:47 David Lee
@ 2026-07-14  6:42 ` Martin Schiller
  2026-07-23  9:58 ` Paolo Abeni
  2026-07-23 10:00 ` patchwork-bot+netdevbpf
  2 siblings, 0 replies; 5+ messages in thread
From: Martin Schiller @ 2026-07-14  6:42 UTC (permalink / raw)
  To: David Lee
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Dominik 'Disconnect3d' Czarnota, linux-x25,
	netdev, linux-kernel, stable

On 2026-07-13 12:47, David Lee wrote:
> x25_kill_by_neigh() walks the global X.25 socket list looking for 
> sockets
> attached to a terminating neighbour. x25_list_lock protects list 
> membership
> while the lookup is in progress, but it does not pin a socket's 
> lifetime
> after the lock is dropped.
> 
> The function currently drops x25_list_lock before calling lock_sock(s). 
> A
> concurrent close can run x25_release(), remove the same socket from
> x25_list, and drop the last socket reference in that window. The 
> neighbour
> teardown path can then lock or inspect a freed struct sock/struct 
> x25_sock.
> 
> Take sock_hold(s) while x25_list_lock still proves that the list entry 
> is
> live, then drop the temporary reference after the socket has been 
> locked,
> rechecked, and released. Recheck x25_sk(s)->neighbour after 
> lock_sock(),
> because another path may have disconnected the socket before this path
> acquired the socket lock. Restart the list walk after each disconnect
> because the list lock was dropped and the previous iterator state may 
> no
> longer be valid.
> 
> A QEMU/KASAN run against origin/master reproduced a slab-use-after-free 
> in
> x25_kill_by_neigh().
> 
> Fixes: 7781607938c8 ("net/x25: Fix null-ptr-deref caused by 
> x25_disconnect")
> Cc: stable@vger.kernel.org
> Signed-off-by: David Lee <david.lee@trailofbits.com>
> Assisted-by: Codex:gpt-5.5
> ---
> Trail of Bits has a reproducer that triggers kernel panic
> demonstrating the bug, and can share it if needed.
> 
> net/x25/af_x25.c |    8 ++++++--
>  1 file changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
> index c31d2af5dd22..8aae9273b7c1 100644
> --- a/net/x25/af_x25.c
> +++ b/net/x25/af_x25.c
> @@ -1768,15 +1768,19 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
>  {
>  	struct sock *s;
> 
> +again:
>  	write_lock_bh(&x25_list_lock);
> 
>  	sk_for_each(s, &x25_list) {
>  		if (x25_sk(s)->neighbour == nb) {
> +			sock_hold(s);
>  			write_unlock_bh(&x25_list_lock);
>  			lock_sock(s);
> -			x25_disconnect(s, ENETUNREACH, 0, 0);
> +			if (x25_sk(s)->neighbour == nb)
> +				x25_disconnect(s, ENETUNREACH, 0, 0);
>  			release_sock(s);
> -			write_lock_bh(&x25_list_lock);
> +			sock_put(s);
> +			goto again;
>  		}
>  	}
>  	write_unlock_bh(&x25_list_lock);

LGTM, Thanks.

Acked-by: Martin Schiller <ms@dev.tdt.de>

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH net] net/x25: fix use-after-free in x25_kill_by_neigh()
  2026-07-13 10:47 David Lee
  2026-07-14  6:42 ` Martin Schiller
@ 2026-07-23  9:58 ` Paolo Abeni
  2026-07-23 10:00 ` patchwork-bot+netdevbpf
  2 siblings, 0 replies; 5+ messages in thread
From: Paolo Abeni @ 2026-07-23  9:58 UTC (permalink / raw)
  To: David Lee, Martin Schiller
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Simon Horman,
	Dominik 'Disconnect3d' Czarnota, linux-x25, netdev,
	linux-kernel, stable

On 7/13/26 12:47 PM, David Lee wrote:
> diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
> index c31d2af5dd22..8aae9273b7c1 100644
> --- a/net/x25/af_x25.c
> +++ b/net/x25/af_x25.c
> @@ -1768,15 +1768,19 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
>  {
>  	struct sock *s;
>  
> +again:
>  	write_lock_bh(&x25_list_lock);
>  
>  	sk_for_each(s, &x25_list) {
>  		if (x25_sk(s)->neighbour == nb) {
> +			sock_hold(s);
>  			write_unlock_bh(&x25_list_lock);
>  			lock_sock(s);
> -			x25_disconnect(s, ENETUNREACH, 0, 0);
> +			if (x25_sk(s)->neighbour == nb)
> +				x25_disconnect(s, ENETUNREACH, 0, 0);
>  			release_sock(s);
> -			write_lock_bh(&x25_list_lock);
> +			sock_put(s);
> +			goto again;
>  		}
>  	}
>  	write_unlock_bh(&x25_list_lock);

Note that sk will stay on list (with NULL neigh) even after disconnect
and the above introduces a quadratic behavior, but I can't find an easy
way to avoid it.

/P


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH net] net/x25: fix use-after-free in x25_kill_by_neigh()
  2026-07-13 10:47 David Lee
  2026-07-14  6:42 ` Martin Schiller
  2026-07-23  9:58 ` Paolo Abeni
@ 2026-07-23 10:00 ` patchwork-bot+netdevbpf
  2 siblings, 0 replies; 5+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-07-23 10:00 UTC (permalink / raw)
  To: David Lee
  Cc: ms, davem, edumazet, kuba, pabeni, horms, dominik.czarnota,
	linux-x25, netdev, linux-kernel, stable

Hello:

This patch was applied to netdev/net.git (main)
by Paolo Abeni <pabeni@redhat.com>:

On Mon, 13 Jul 2026 10:47:50 +0000 you wrote:
> x25_kill_by_neigh() walks the global X.25 socket list looking for sockets
> attached to a terminating neighbour. x25_list_lock protects list membership
> while the lookup is in progress, but it does not pin a socket's lifetime
> after the lock is dropped.
> 
> The function currently drops x25_list_lock before calling lock_sock(s). A
> concurrent close can run x25_release(), remove the same socket from
> x25_list, and drop the last socket reference in that window. The neighbour
> teardown path can then lock or inspect a freed struct sock/struct x25_sock.
> 
> [...]

Here is the summary with links:
  - [net] net/x25: fix use-after-free in x25_kill_by_neigh()
    https://git.kernel.org/netdev/net/c/5499e0602d2f

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH net] net/x25: fix use-after-free in x25_kill_by_neigh()
@ 2026-07-26  5:33 Baul Lee
  0 siblings, 0 replies; 5+ messages in thread
From: Baul Lee @ 2026-07-26  5:33 UTC (permalink / raw)
  To: netdev
  Cc: ms, davem, edumazet, kuba, pabeni, horms, federico.kirschbaum,
	Baul Lee, stable

x25_kill_by_neigh() walks x25_list under x25_list_lock and, for every
socket bound to the dying neighbour, drops the lock to call lock_sock() /
x25_disconnect() / release_sock() before re-acquiring it and continuing
the iteration.  It holds no reference on the socket across this window.

A concurrent x25_release() on the same socket can therefore
sk_del_node() and free it while the lock is dropped, so the following
lock_sock(s) operates on freed memory; the sk_for_each() cursor can also
be advanced through the freed node once the lock is re-acquired.  Either
way an X.25 device going down races a socket close into a slab
use-after-free.

Take a reference with sock_hold() before dropping the lock so the socket
stays alive across lock_sock()/x25_disconnect()/release_sock(), and drop
it with sock_put() after the lock is re-acquired.  Because the socket may
have been unlinked from x25_list during the window, restart the walk from
the head instead of advancing from the (possibly unlinked) cursor;
x25_disconnect() clears x25_sk(s)->neighbour, so an already-handled
socket no longer matches and the walk terminates.  This mirrors the
reference-holding list walkers already used elsewhere in this file.

Discovered by XBOW, triaged by Baul Lee <baul.lee@xbow.com>
Reported privately to the maintainers on 2026-07-09 with root-cause
analysis, a PoC, a KASAN log and this fix; posting to the list was
requested as the follow-up.

Fixes: 7781607938c8 ("net/x25: Fix null-ptr-deref caused by x25_disconnect")
Reported-by: Federico Kirschbaum <federico.kirschbaum@xbow.com>
Reported-by: Baul Lee <baul.lee@xbow.com>
Cc: stable@vger.kernel.org
Signed-off-by: Baul Lee <baul.lee@xbow.com>
---
 net/x25/af_x25.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
index c31d2af5dd22..68c396dc4307 100644
--- a/net/x25/af_x25.c
+++ b/net/x25/af_x25.c
@@ -1770,13 +1770,17 @@ void x25_kill_by_neigh(struct x25_neigh *nb)
 
 	write_lock_bh(&x25_list_lock);
 
+restart:
 	sk_for_each(s, &x25_list) {
 		if (x25_sk(s)->neighbour == nb) {
+			sock_hold(s);
 			write_unlock_bh(&x25_list_lock);
 			lock_sock(s);
 			x25_disconnect(s, ENETUNREACH, 0, 0);
 			release_sock(s);
 			write_lock_bh(&x25_list_lock);
+			sock_put(s);
+			goto restart;
 		}
 	}
 	write_unlock_bh(&x25_list_lock);
-- 
2.50.1 (Apple Git-155)


^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-07-26  5:33 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-26  5:33 [PATCH net] net/x25: fix use-after-free in x25_kill_by_neigh() Baul Lee
  -- strict thread matches above, loose matches on Subject: below --
2026-07-13 10:47 David Lee
2026-07-14  6:42 ` Martin Schiller
2026-07-23  9:58 ` Paolo Abeni
2026-07-23 10:00 ` patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox