public inbox for linux-hams@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] rose: Fix use-after-free in rose_timer_expiry
@ 2026-01-17  6:39 Deepanshu Kartikey
  2026-01-19 20:19 ` F6BVP
  0 siblings, 1 reply; 8+ messages in thread
From: Deepanshu Kartikey @ 2026-01-17  6:39 UTC (permalink / raw)
  To: davem, edumazet, kuba, pabeni, horms, mingo
  Cc: takamitz, tglx, linux-hams, netdev, linux-kernel,
	Deepanshu Kartikey, syzbot+62360d745376b40120b5

A use-after-free bug can occur when rose_timer_expiry() in state
ROSE_STATE_2 releases the rose_neigh structure via rose_neigh_put(),
while the neighbour's timers (ftimer and t0timer) are still active
or being processed.

The race occurs between:
1. rose_timer_expiry() freeing rose_neigh via rose_neigh_put()
2. rose_t0timer_expiry() attempting to rearm itself via
   rose_start_t0timer(), which calls add_timer() on the freed
   structure

This leads to a KASAN use-after-free report when the timer code
attempts to access the freed memory:

BUG: KASAN: slab-use-after-free in timer_is_static_object+0x80/0x90
Read of size 8 at addr ffff88807e5e8498 by task syz.4.6813/32052

The buggy address is located 152 bytes inside of freed 512-byte
region allocated by rose_add_node().

Fix this by calling timer_shutdown() on both ftimer and t0timer
before releasing the rose_neigh structure. timer_shutdown() ensures
the timers are stopped and prevents them from being rearmed, even
if their callbacks are currently executing.

This fix is based on code analysis as no C reproducer is available
for this issue.

Reported-by: syzbot+62360d745376b40120b5@syzkaller.appspotmail.com
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
 net/rose/rose_timer.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c
index bb60a1654d61..6e6483c024fa 100644
--- a/net/rose/rose_timer.c
+++ b/net/rose/rose_timer.c
@@ -180,6 +180,8 @@ static void rose_timer_expiry(struct timer_list *t)
 		break;
 
 	case ROSE_STATE_2:	/* T3 */
+		timer_shutdown(&rose->neighbour->ftimer);
+		timer_shutdown(&rose->neighbour->t0timer);
 		rose_neigh_put(rose->neighbour);
 		rose_disconnect(sk, ETIMEDOUT, -1, -1);
 		break;
-- 
2.43.0


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

* Re: [PATCH] rose: Fix use-after-free in rose_timer_expiry
  2026-01-17  6:39 [PATCH] rose: Fix use-after-free in rose_timer_expiry Deepanshu Kartikey
@ 2026-01-19 20:19 ` F6BVP
  2026-04-13 17:42   ` [PATCH net-next 1/3] rose: fix race between loopback timer and module removal f6bvp
  0 siblings, 1 reply; 8+ messages in thread
From: F6BVP @ 2026-01-19 20:19 UTC (permalink / raw)
  To: kartikey406
  Cc: davem, edumazet, horms, kuba, linux-hams, linux-kernel, mingo,
	netdev, pabeni, syzbot+62360d745376b40120b5, takamitz, tglx

Patch applied to rose_timer.c

Result is interesting even if

rose module cannot still be removed with rmmod command.

For a very long time rmmod would freeze the console and the task could 
not be killed.

With the patch applied rmmod command is not blocked anymore.

However rose module is not removed probably because of a wrong refcount ?

lsmod | grep rose
rose                  184320  -1
ax25                  217088  1 rose

Bernard, f6bvp


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

* [PATCH net-next 1/3] rose: fix race between loopback timer and module removal
  2026-01-19 20:19 ` F6BVP
@ 2026-04-13 17:42   ` f6bvp
  2026-04-13 17:42     ` [PATCH net-next 2/3] rose: clear neighbour pointer after rose_neigh_put() in state machines f6bvp
                       ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: f6bvp @ 2026-04-13 17:42 UTC (permalink / raw)
  To: linux-hams; +Cc: netdev, edumazet, pabeni, f6bvp

rose_loopback_clear() used timer_delete() which returns immediately
without waiting for any running callback to complete.  If the timer
fired concurrently with module removal, rose_loopback_timer() would
access rose_loopback_neigh after it was freed, causing a use-after-free.

Three changes fix the race:

1. Add a loopback_stopping atomic flag.  rose_loopback_timer() checks
   this at entry and mid-loop; when set it drains the queue and bails
   out without re-arming the timer.

2. Switch rose_loopback_clear() to timer_delete_sync() so it blocks
   until any in-flight callback has returned.

3. Wrap the timer body with rose_neigh_hold()/rose_neigh_put() so the
   loopback neighbour cannot be freed while the callback is running.

Also fix a pre-existing bug: dev_put(dev) was only called on the
failure path of rose_rx_call_request(); it is now called unconditionally
so the device reference is always released.

Remove a dead check (!neigh->dev && !neigh->loopback) that can never
be true for the loopback neighbour, which always has loopback=1.

Signed-off-by: f6bvp <bernard.f6bvp@gmail.com>
---
 net/rose/rose_loopback.c | 53 +++++++++++++++++++++++++++-------------
 1 file changed, 36 insertions(+), 17 deletions(-)

diff --git a/net/rose/rose_loopback.c b/net/rose/rose_loopback.c
index b538e39b3df5..80d7879ef36a 100644
--- a/net/rose/rose_loopback.c
+++ b/net/rose/rose_loopback.c
@@ -12,13 +12,15 @@
 #include <net/rose.h>
 #include <linux/init.h>
 
-static struct sk_buff_head loopback_queue;
 #define ROSE_LOOPBACK_LIMIT 1000
-static struct timer_list loopback_timer;
 
+static struct timer_list loopback_timer;
+static struct sk_buff_head loopback_queue;
 static void rose_set_loopback_timer(void);
 static void rose_loopback_timer(struct timer_list *unused);
 
+static atomic_t loopback_stopping = ATOMIC_INIT(0);
+
 void rose_loopback_init(void)
 {
 	skb_queue_head_init(&loopback_queue);
@@ -66,10 +68,25 @@ static void rose_loopback_timer(struct timer_list *unused)
 	unsigned int lci_i, lci_o;
 	int count;
 
+	if (atomic_read(&loopback_stopping))
+		return;
+
+	if (rose_loopback_neigh)
+		rose_neigh_hold(rose_loopback_neigh);
+	else
+		return;
+
 	for (count = 0; count < ROSE_LOOPBACK_LIMIT; count++) {
 		skb = skb_dequeue(&loopback_queue);
 		if (!skb)
-			return;
+			goto out;
+
+		if (atomic_read(&loopback_stopping)) {
+			kfree_skb(skb);
+			skb_queue_purge(&loopback_queue);
+			goto out;
+		}
+
 		if (skb->len < ROSE_MIN_LEN) {
 			kfree_skb(skb);
 			continue;
@@ -96,27 +113,24 @@ static void rose_loopback_timer(struct timer_list *unused)
 		}
 
 		if (frametype == ROSE_CALL_REQUEST) {
-			if (!rose_loopback_neigh->dev &&
-			    !rose_loopback_neigh->loopback) {
-				kfree_skb(skb);
-				continue;
-			}
-
 			dev = rose_dev_get(dest);
 			if (!dev) {
 				kfree_skb(skb);
 				continue;
 			}
 
-			if (rose_rx_call_request(skb, dev, rose_loopback_neigh, lci_o) == 0) {
-				dev_put(dev);
+			if (rose_rx_call_request(skb, dev, rose_loopback_neigh, lci_o) == 0)
 				kfree_skb(skb);
-			}
+			dev_put(dev);
 		} else {
 			kfree_skb(skb);
 		}
 	}
-	if (!skb_queue_empty(&loopback_queue))
+
+out:
+	rose_neigh_put(rose_loopback_neigh);
+
+	if (!atomic_read(&loopback_stopping) && !skb_queue_empty(&loopback_queue))
 		mod_timer(&loopback_timer, jiffies + 1);
 }
 
@@ -124,10 +138,15 @@ void __exit rose_loopback_clear(void)
 {
 	struct sk_buff *skb;
 
-	timer_delete(&loopback_timer);
+	atomic_set(&loopback_stopping, 1);
+	/* Pairs with atomic_read() in rose_loopback_timer(): ensure the
+	 * stopping flag is visible before we cancel, so a concurrent
+	 * callback aborts its loop early rather than re-arming the timer.
+	 */
+	smp_mb();
 
-	while ((skb = skb_dequeue(&loopback_queue)) != NULL) {
-		skb->sk = NULL;
+	timer_delete_sync(&loopback_timer);
+
+	while ((skb = skb_dequeue(&loopback_queue)) != NULL)
 		kfree_skb(skb);
-	}
 }
-- 
2.51.0


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

* [PATCH net-next 2/3] rose: clear neighbour pointer after rose_neigh_put() in state machines
  2026-04-13 17:42   ` [PATCH net-next 1/3] rose: fix race between loopback timer and module removal f6bvp
@ 2026-04-13 17:42     ` f6bvp
  2026-04-13 20:53       ` Jakub Kicinski
  2026-04-13 17:42     ` [PATCH net-next 3/3] rose: guard rose_neigh_put() against NULL in timer expiry f6bvp
  2026-04-13 21:21     ` [PATCH net-next 1/3] rose: fix race between loopback timer and module removal Andrew Lunn
  2 siblings, 1 reply; 8+ messages in thread
From: f6bvp @ 2026-04-13 17:42 UTC (permalink / raw)
  To: linux-hams; +Cc: netdev, edumazet, pabeni, f6bvp

After releasing a neighbour reference with rose_neigh_put() in the
ROSE state machines, the pointer in rose_sock was left dangling.
A subsequent code path could dereference the freed neighbour, causing
a use-after-free.

Set rose->neighbour to NULL immediately after each rose_neigh_put()
call in rose_state1_machine() through rose_state5_machine().

Signed-off-by: f6bvp <bernard.f6bvp@gmail.com>
---
 net/rose/rose_in.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/net/rose/rose_in.c b/net/rose/rose_in.c
index 0276b393f0e5..622527f1354f 100644
--- a/net/rose/rose_in.c
+++ b/net/rose/rose_in.c
@@ -57,6 +57,7 @@ static int rose_state1_machine(struct sock *sk, struct sk_buff *skb, int framety
 		rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
 		rose_disconnect(sk, ECONNREFUSED, skb->data[3], skb->data[4]);
 		rose_neigh_put(rose->neighbour);
+		rose->neighbour = NULL;
 		break;
 
 	default:
@@ -80,11 +81,13 @@ static int rose_state2_machine(struct sock *sk, struct sk_buff *skb, int framety
 		rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
 		rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
 		rose_neigh_put(rose->neighbour);
+		rose->neighbour = NULL;
 		break;
 
 	case ROSE_CLEAR_CONFIRMATION:
 		rose_disconnect(sk, 0, -1, -1);
 		rose_neigh_put(rose->neighbour);
+		rose->neighbour = NULL;
 		break;
 
 	default:
@@ -122,6 +125,7 @@ static int rose_state3_machine(struct sock *sk, struct sk_buff *skb, int framety
 		rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
 		rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
 		rose_neigh_put(rose->neighbour);
+		rose->neighbour = NULL;
 		break;
 
 	case ROSE_RR:
@@ -235,6 +239,7 @@ static int rose_state4_machine(struct sock *sk, struct sk_buff *skb, int framety
 		rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
 		rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
 		rose_neigh_put(rose->neighbour);
+		rose->neighbour = NULL;
 		break;
 
 	default:
@@ -255,6 +260,7 @@ static int rose_state5_machine(struct sock *sk, struct sk_buff *skb, int framety
 		rose_write_internal(sk, ROSE_CLEAR_CONFIRMATION);
 		rose_disconnect(sk, 0, skb->data[3], skb->data[4]);
 		rose_neigh_put(rose_sk(sk)->neighbour);
+		rose_sk(sk)->neighbour = NULL;
 	}
 
 	return 0;
-- 
2.51.0


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

* [PATCH net-next 3/3] rose: guard rose_neigh_put() against NULL in timer expiry
  2026-04-13 17:42   ` [PATCH net-next 1/3] rose: fix race between loopback timer and module removal f6bvp
  2026-04-13 17:42     ` [PATCH net-next 2/3] rose: clear neighbour pointer after rose_neigh_put() in state machines f6bvp
@ 2026-04-13 17:42     ` f6bvp
  2026-04-13 21:21     ` [PATCH net-next 1/3] rose: fix race between loopback timer and module removal Andrew Lunn
  2 siblings, 0 replies; 8+ messages in thread
From: f6bvp @ 2026-04-13 17:42 UTC (permalink / raw)
  To: linux-hams; +Cc: netdev, edumazet, pabeni, f6bvp

In rose_timer_expiry(), ROSE_STATE_2 calls rose_neigh_put() on
rose->neighbour without checking whether it is NULL first.  The pointer
can be NULL if the connection was already being torn down by a
concurrent code path (e.g. rose_kill_by_neigh()), leading to a
NULL-pointer dereference.

Add a NULL check before the put and clear the pointer afterwards.

Signed-off-by: f6bvp <bernard.f6bvp@gmail.com>
---
 net/rose/rose_timer.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/net/rose/rose_timer.c b/net/rose/rose_timer.c
index bb60a1654d61..d997d24ab081 100644
--- a/net/rose/rose_timer.c
+++ b/net/rose/rose_timer.c
@@ -180,7 +180,10 @@ static void rose_timer_expiry(struct timer_list *t)
 		break;
 
 	case ROSE_STATE_2:	/* T3 */
-		rose_neigh_put(rose->neighbour);
+		if (rose->neighbour) {
+			rose_neigh_put(rose->neighbour);
+			rose->neighbour = NULL;
+		}
 		rose_disconnect(sk, ETIMEDOUT, -1, -1);
 		break;
 
-- 
2.51.0


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

* Re: [PATCH net-next 2/3] rose: clear neighbour pointer after rose_neigh_put() in state machines
  2026-04-13 17:42     ` [PATCH net-next 2/3] rose: clear neighbour pointer after rose_neigh_put() in state machines f6bvp
@ 2026-04-13 20:53       ` Jakub Kicinski
  0 siblings, 0 replies; 8+ messages in thread
From: Jakub Kicinski @ 2026-04-13 20:53 UTC (permalink / raw)
  To: f6bvp; +Cc: linux-hams, netdev, edumazet, pabeni

On Mon, 13 Apr 2026 19:42:37 +0200 f6bvp wrote:
> Signed-off-by: f6bvp <bernard.f6bvp@gmail.com>

Human name is required when authoring patches.
Please do not post patches in reply to existing threads. See:
https://www.kernel.org/doc/html/next/process/maintainer-netdev.html
-- 
pw-bot: cr

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

* Re: [PATCH net-next 1/3] rose: fix race between loopback timer and module removal
  2026-04-13 17:42   ` [PATCH net-next 1/3] rose: fix race between loopback timer and module removal f6bvp
  2026-04-13 17:42     ` [PATCH net-next 2/3] rose: clear neighbour pointer after rose_neigh_put() in state machines f6bvp
  2026-04-13 17:42     ` [PATCH net-next 3/3] rose: guard rose_neigh_put() against NULL in timer expiry f6bvp
@ 2026-04-13 21:21     ` Andrew Lunn
  2026-04-13 21:34       ` Jakub Kicinski
  2 siblings, 1 reply; 8+ messages in thread
From: Andrew Lunn @ 2026-04-13 21:21 UTC (permalink / raw)
  To: f6bvp; +Cc: linux-hams, netdev, edumazet, pabeni

On Mon, Apr 13, 2026 at 07:42:36PM +0200, f6bvp wrote:
> rose_loopback_clear() used timer_delete() which returns immediately
> without waiting for any running callback to complete.  If the timer
> fired concurrently with module removal, rose_loopback_timer() would
> access rose_loopback_neigh after it was freed, causing a use-after-free.
> 
> Three changes fix the race:
> 
> 1. Add a loopback_stopping atomic flag.  rose_loopback_timer() checks
>    this at entry and mid-loop; when set it drains the queue and bails
>    out without re-arming the timer.
> 
> 2. Switch rose_loopback_clear() to timer_delete_sync() so it blocks
>    until any in-flight callback has returned.
> 
> 3. Wrap the timer body with rose_neigh_hold()/rose_neigh_put() so the
>    loopback neighbour cannot be freed while the callback is running.
> 
> Also fix a pre-existing bug: dev_put(dev) was only called on the
> failure path of rose_rx_call_request(); it is now called unconditionally
> so the device reference is always released.

Hi Barnard

Thanks for the patches.

A few process points.

We prefer lots of small patches, with good commit messages, which are
obviously correct. When i see a list like this, it makes me think the
patch can be split up into smaller patches.

When you have a patch series, please include a patch 0/X which
explains the big picture.

net-next is current closed for the merge window. You can post patches
as RFC, but don't post anything expecting it to be merged.

There is more information here:

https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html

	Andrew

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

* Re: [PATCH net-next 1/3] rose: fix race between loopback timer and module removal
  2026-04-13 21:21     ` [PATCH net-next 1/3] rose: fix race between loopback timer and module removal Andrew Lunn
@ 2026-04-13 21:34       ` Jakub Kicinski
  0 siblings, 0 replies; 8+ messages in thread
From: Jakub Kicinski @ 2026-04-13 21:34 UTC (permalink / raw)
  To: Andrew Lunn, f6bvp; +Cc: linux-hams, netdev, edumazet, pabeni

On Mon, 13 Apr 2026 23:21:16 +0200 Andrew Lunn wrote:
> net-next is current closed for the merge window. You can post patches
> as RFC, but don't post anything expecting it to be merged.

FWIW I assumed this were fixes, in which case the closing of the trees
would not apply, the patches should be marked as "PATCH net" and have 
a Fixes tag.

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

end of thread, other threads:[~2026-04-13 21:34 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-17  6:39 [PATCH] rose: Fix use-after-free in rose_timer_expiry Deepanshu Kartikey
2026-01-19 20:19 ` F6BVP
2026-04-13 17:42   ` [PATCH net-next 1/3] rose: fix race between loopback timer and module removal f6bvp
2026-04-13 17:42     ` [PATCH net-next 2/3] rose: clear neighbour pointer after rose_neigh_put() in state machines f6bvp
2026-04-13 20:53       ` Jakub Kicinski
2026-04-13 17:42     ` [PATCH net-next 3/3] rose: guard rose_neigh_put() against NULL in timer expiry f6bvp
2026-04-13 21:21     ` [PATCH net-next 1/3] rose: fix race between loopback timer and module removal Andrew Lunn
2026-04-13 21:34       ` Jakub Kicinski

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