]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
7.0-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Jun 2026 11:02:53 +0000 (12:02 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Jun 2026 11:02:53 +0000 (12:02 +0100)
added patches:
rose-cancel-neighbour-timers-in-rose_neigh_put-before-freeing.patch
rose-clear-neighbour-pointer-after-rose_neigh_put-in-state-machines.patch
rose-clear-neighbour-pointer-in-rose_kill_by_device.patch
rose-disconnect-orphaned-state_2-sockets-when-device-is-gone.patch
rose-don-t-free-fd-owned-sockets-when-reaping-in-the-heartbeat.patch
rose-drop-call_request-in-loopback-timer-when-device-is-not-running.patch
rose-fix-dev_put-leak-in-rose_loopback_timer.patch
rose-fix-netdev-double-hold-in-rose_make_new.patch
rose-fix-netdev-double-hold-in-rose_rx_call_request.patch
rose-fix-notifier-unregistered-too-early-in-rose_exit.patch
rose-fix-race-between-loopback-timer-and-module-removal.patch
rose-guard-rose_neigh_put-against-null-in-timer-expiry.patch
rose-hold-loopback-neighbour-reference-across-timer-callback.patch
rose-release-netdev-ref-and-destroy-orphaned-incoming-sockets.patch
rose-set-sock_destroy-in-rose_kill_by_device-for-prompt-cleanup.patch

16 files changed:
queue-7.0/rose-cancel-neighbour-timers-in-rose_neigh_put-before-freeing.patch [new file with mode: 0644]
queue-7.0/rose-clear-neighbour-pointer-after-rose_neigh_put-in-state-machines.patch [new file with mode: 0644]
queue-7.0/rose-clear-neighbour-pointer-in-rose_kill_by_device.patch [new file with mode: 0644]
queue-7.0/rose-disconnect-orphaned-state_2-sockets-when-device-is-gone.patch [new file with mode: 0644]
queue-7.0/rose-don-t-free-fd-owned-sockets-when-reaping-in-the-heartbeat.patch [new file with mode: 0644]
queue-7.0/rose-drop-call_request-in-loopback-timer-when-device-is-not-running.patch [new file with mode: 0644]
queue-7.0/rose-fix-dev_put-leak-in-rose_loopback_timer.patch [new file with mode: 0644]
queue-7.0/rose-fix-netdev-double-hold-in-rose_make_new.patch [new file with mode: 0644]
queue-7.0/rose-fix-netdev-double-hold-in-rose_rx_call_request.patch [new file with mode: 0644]
queue-7.0/rose-fix-notifier-unregistered-too-early-in-rose_exit.patch [new file with mode: 0644]
queue-7.0/rose-fix-race-between-loopback-timer-and-module-removal.patch [new file with mode: 0644]
queue-7.0/rose-guard-rose_neigh_put-against-null-in-timer-expiry.patch [new file with mode: 0644]
queue-7.0/rose-hold-loopback-neighbour-reference-across-timer-callback.patch [new file with mode: 0644]
queue-7.0/rose-release-netdev-ref-and-destroy-orphaned-incoming-sockets.patch [new file with mode: 0644]
queue-7.0/rose-set-sock_destroy-in-rose_kill_by_device-for-prompt-cleanup.patch [new file with mode: 0644]
queue-7.0/series

diff --git a/queue-7.0/rose-cancel-neighbour-timers-in-rose_neigh_put-before-freeing.patch b/queue-7.0/rose-cancel-neighbour-timers-in-rose_neigh_put-before-freeing.patch
new file mode 100644 (file)
index 0000000..1562502
--- /dev/null
@@ -0,0 +1,48 @@
+From 9b222cb1d23ff210975e9df5ebab7b011acb6fad Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Sun, 31 May 2026 15:41:45 +0200
+Subject: rose: cancel neighbour timers in rose_neigh_put() before freeing
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit 9b222cb1d23ff210975e9df5ebab7b011acb6fad upstream.
+
+rose_neigh_put() kfree()s the neighbour but never cancels its ftimer and
+t0timer. Until now every caller that dropped the final reference first
+called rose_remove_neigh(), which deletes those timers. The socket
+heartbeat reaping path drops the last reference directly, so a neighbour
+could be freed with t0timer still armed -- it re-arms itself in
+rose_t0timer_expiry() -- leading to a use-after-free write in
+enqueue_timer().
+
+Cancel both timers with timer_delete_sync() (the synchronous variant, to
+wait out a concurrently running, self-rearming handler) in the
+refcount-zero branch of rose_neigh_put().
+
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/net/rose.h |   12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/include/net/rose.h
++++ b/include/net/rose.h
+@@ -160,6 +160,18 @@ static inline void rose_neigh_hold(struc
+ static inline void rose_neigh_put(struct rose_neigh *rose_neigh)
+ {
+       if (refcount_dec_and_test(&rose_neigh->use)) {
++              /* We are dropping the last reference, so we are about to free the
++               * neighbour.  Its timers may still be armed -- t0timer in particular
++               * re-arms itself in rose_t0timer_expiry().  rose_remove_neigh()
++               * cancels them before its own put, but callers that drop the final
++               * reference without first calling rose_remove_neigh() (the socket
++               * heartbeat reaping path) would otherwise kfree() a neighbour with a
++               * live timer -> use-after-free.  timer_delete_sync() (not the async
++               * variant) is required: it waits out a concurrently running handler
++               * and loops until the self-rearming timer stays stopped.
++               */
++              timer_delete_sync(&rose_neigh->ftimer);
++              timer_delete_sync(&rose_neigh->t0timer);
+               if (rose_neigh->ax25)
+                       ax25_cb_put(rose_neigh->ax25);
+               kfree(rose_neigh->digipeat);
diff --git a/queue-7.0/rose-clear-neighbour-pointer-after-rose_neigh_put-in-state-machines.patch b/queue-7.0/rose-clear-neighbour-pointer-after-rose_neigh_put-in-state-machines.patch
new file mode 100644 (file)
index 0000000..b16cd80
--- /dev/null
@@ -0,0 +1,73 @@
+From e8eb0c6faa8849ba7769516c1a8c84d9f612acf6 Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Sat, 16 May 2026 12:10:38 +0200
+Subject: rose: clear neighbour pointer after rose_neigh_put() in state machines
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit e8eb0c6faa8849ba7769516c1a8c84d9f612acf6 upstream.
+
+After calling rose_neigh_put() in rose_state1_machine() through
+rose_state5_machine(), rose->neighbour was left pointing at the
+potentially freed neighbour structure.  A subsequent timer expiry or
+concurrent teardown path could dereference the stale pointer, causing
+a use-after-free.
+
+Set rose->neighbour to NULL immediately after each rose_neigh_put()
+call in the state machine functions.
+
+Fixes: d860d1faa6b2 ("net: rose: convert 'use' field to refcount_t")
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/rose_in.c |    6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/net/rose/rose_in.c
++++ b/net/rose/rose_in.c
+@@ -57,6 +57,7 @@ static int rose_state1_machine(struct so
+               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 so
+               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 so
+               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 so
+               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 so
+               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;
diff --git a/queue-7.0/rose-clear-neighbour-pointer-in-rose_kill_by_device.patch b/queue-7.0/rose-clear-neighbour-pointer-in-rose_kill_by_device.patch
new file mode 100644 (file)
index 0000000..3ded724
--- /dev/null
@@ -0,0 +1,44 @@
+From 606e42d195b467480d4d405f8814c48d1651a76a Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Sun, 31 May 2026 15:41:45 +0200
+Subject: rose: clear neighbour pointer in rose_kill_by_device()
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit 606e42d195b467480d4d405f8814c48d1651a76a upstream.
+
+rose_kill_by_device() drops the neighbour reference but leaves
+rose->neighbour pointing at it, unlike every other rose_neigh_put() site
+(see "rose: clear neighbour pointer after rose_neigh_put() in state
+machines"). The heartbeat STATE_0 reaping path then puts the same
+neighbour a second time, causing a rose_neigh refcount underflow and a
+use-after-free.
+
+Set rose->neighbour = NULL after the put, restoring the invariant.
+
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/af_rose.c |   10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+--- a/net/rose/af_rose.c
++++ b/net/rose/af_rose.c
+@@ -216,8 +216,16 @@ start:
+                        * looping forever in ROSE_STATE_0 with no owner.
+                        */
+                       sock_set_flag(sk, SOCK_DESTROY);
+-                      if (rose->neighbour)
++                      if (rose->neighbour) {
+                               rose_neigh_put(rose->neighbour);
++                              /* Clear the pointer after dropping the reference, as
++                               * every other rose_neigh_put() site does.  Otherwise
++                               * rose_heartbeat_expiry() (STATE_0 reaping) sees a stale
++                               * rose->neighbour and puts it a second time -> rose_neigh
++                               * refcount underflow / use-after-free.
++                               */
++                              rose->neighbour = NULL;
++                      }
+                       netdev_put(rose->device, &rose->dev_tracker);
+                       rose->device = NULL;
+               }
diff --git a/queue-7.0/rose-disconnect-orphaned-state_2-sockets-when-device-is-gone.patch b/queue-7.0/rose-disconnect-orphaned-state_2-sockets-when-device-is-gone.patch
new file mode 100644 (file)
index 0000000..b0339ae
--- /dev/null
@@ -0,0 +1,50 @@
+From d4f4cf9f09a3f5fafa8f09110a7c1b5d10f2f261 Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Thu, 28 May 2026 17:38:18 +0200
+Subject: rose: disconnect orphaned STATE_2 sockets when device is gone
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit d4f4cf9f09a3f5fafa8f09110a7c1b5d10f2f261 upstream.
+
+When ax25stop brings down ROSE interfaces, sockets in ROSE_STATE_2
+(awaiting CLEAR CONFIRM) whose device pointer is already NULL are not
+reached by rose_kill_by_device() and wait for T3 (up to 180s) before
+self-cleaning via rose_timer_expiry().  This keeps the rose module
+usecount at 1, blocking rmmod for the full T3 duration.
+
+In rose_heartbeat_expiry(), detect ROSE_STATE_2 sockets with no device,
+cancel T3, release the neighbour reference, and call rose_disconnect()
++ sock_set_flag(SOCK_DESTROY).  The next heartbeat tick (<=5s) then
+destroys the socket via the existing ROSE_STATE_0/SOCK_DESTROY path,
+allowing clean module unload within 10s instead of up to 180s.
+
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/rose_timer.c |   14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/net/rose/rose_timer.c
++++ b/net/rose/rose_timer.c
+@@ -139,6 +139,20 @@ static void rose_heartbeat_expiry(struct
+               }
+               break;
++      case ROSE_STATE_2:
++              /* Device gone before CLEAR CONFIRM arrived: stop waiting for T3
++               * and disconnect now instead of blocking rmmod for up to 180s. */
++              if (!rose->device) {
++                      rose_stop_timer(sk);
++                      if (rose->neighbour) {
++                              rose_neigh_put(rose->neighbour);
++                              rose->neighbour = NULL;
++                      }
++                      rose_disconnect(sk, ENETDOWN, -1, -1);
++                      sock_set_flag(sk, SOCK_DESTROY);
++              }
++              break;
++
+       case ROSE_STATE_3:
+               /*
+                * Check for the state of the receive buffer.
diff --git a/queue-7.0/rose-don-t-free-fd-owned-sockets-when-reaping-in-the-heartbeat.patch b/queue-7.0/rose-don-t-free-fd-owned-sockets-when-reaping-in-the-heartbeat.patch
new file mode 100644 (file)
index 0000000..96c615e
--- /dev/null
@@ -0,0 +1,100 @@
+From 56576518920edd7b6c3479477d8d490fe2ebdaaa Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Sun, 31 May 2026 15:41:45 +0200
+Subject: rose: don't free fd-owned sockets when reaping in the heartbeat
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit 56576518920edd7b6c3479477d8d490fe2ebdaaa upstream.
+
+The heartbeat reaps orphaned ROSE sockets after their bound device goes
+down. A socket still attached to a struct socket (sk->sk_socket != NULL --
+e.g. an incoming connection an fpad client has accepted and kept open) is
+owned by that userspace fd: rose_release() frees it on close(). Freeing it
+from the heartbeat left the fd dangling, so the eventual close() touched
+freed memory -- slab-use-after-free in rose_release().
+
+Reap only sockets with sk->sk_socket == NULL (unaccepted incoming
+connections and post-close orphans). For an fd-owned socket whose device
+went down, disconnect it and fall through to the switch so close() does
+the teardown. Also release the neighbour reference held by orphaned
+incoming sockets before tearing them down.
+
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/rose_timer.c |   57 +++++++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 56 insertions(+), 1 deletion(-)
+
+--- a/net/rose/rose_timer.c
++++ b/net/rose/rose_timer.c
+@@ -126,13 +126,68 @@ static void rose_heartbeat_expiry(struct
+               sk_reset_timer(sk, &sk->sk_timer, jiffies + HZ/20);
+               goto out;
+       }
++
++      /* The bound device went down while we still hold a reference on it.
++       * This catches the narrow race where rose_loopback_timer() created a
++       * socket in the window after rose_kill_by_device()'s NETDEV_DOWN sweep
++       * but before rose_insert_socket() -- leaving a STATE_3 socket that no
++       * other branch reaps.  A down device means the link is dead, so tear
++       * the socket down regardless of state.  rose_destroy_socket() releases
++       * the held netdev reference (rose->device still set).
++       */
++      if (rose->device && !netif_running(rose->device)) {
++              if (rose->neighbour) {
++                      rose_neigh_put(rose->neighbour);
++                      rose->neighbour = NULL;
++              }
++              rose_disconnect(sk, ENETDOWN, -1, -1);
++
++              /* Only reap the socket if userspace no longer holds it.  A socket
++               * still attached to a struct socket (sk->sk_socket != NULL -- e.g.
++               * a connection an fpad client has accepted and kept open) is owned
++               * by that fd: rose_release() will destroy it on close().  Dropping
++               * the last reference here leaves the open fd dangling, so the
++               * eventual close() touches freed memory -> slab-use-after-free in
++               * rose_release().  Unaccepted incoming sockets and post-close
++               * orphans have sk->sk_socket == NULL and stay safe to reap here.
++               */
++              if (!sk->sk_socket) {
++                      sock_set_flag(sk, SOCK_DESTROY);
++                      bh_unlock_sock(sk);
++                      rose_destroy_socket(sk);
++                      sock_put(sk);
++                      return;
++              }
++
++              /* Owned by userspace: the link is down and the socket is now
++               * disconnected (rose_disconnect() moved it to STATE_0).  Fall
++               * through to the switch, which re-arms the heartbeat; the close()
++               * will tear the socket down. */
++      }
++
+       switch (rose->state) {
+       case ROSE_STATE_0:
+               /* Destroy any orphaned STATE_0 socket: either explicitly
+                * flagged SOCK_DESTROY, or SOCK_DEAD (covers both unaccepted
+                * incoming connections and listening sockets whose link died).
+                */
+-              if (sock_flag(sk, SOCK_DESTROY) || sock_flag(sk, SOCK_DEAD)) {
++              if ((sock_flag(sk, SOCK_DESTROY) || sock_flag(sk, SOCK_DEAD)) &&
++                  !sk->sk_socket) {
++                      /* Reap only orphaned sockets (sk->sk_socket == NULL).  A
++                       * socket still owned by a userspace fd reaches here via the
++                       * STATE_2 device-gone branch, which sets SOCK_DESTROY without
++                       * knowing about the fd; freeing it would race rose_release()
++                       * at close() -> use-after-free.  Leave it for close().
++                       *
++                       * Orphaned incoming sockets (rose_rx_call_request) hold a
++                       * neighbour reference; release it before teardown, as the
++                       * STATE_2 and device-down branches do.  rose_destroy_socket()
++                       * does not drop it.
++                       */
++                      if (rose->neighbour) {
++                              rose_neigh_put(rose->neighbour);
++                              rose->neighbour = NULL;
++                      }
+                       bh_unlock_sock(sk);
+                       rose_destroy_socket(sk);
+                       sock_put(sk);
diff --git a/queue-7.0/rose-drop-call_request-in-loopback-timer-when-device-is-not-running.patch b/queue-7.0/rose-drop-call_request-in-loopback-timer-when-device-is-not-running.patch
new file mode 100644 (file)
index 0000000..4607c6e
--- /dev/null
@@ -0,0 +1,55 @@
+From cf5567a2652e44866eae8987dff4c1ea507680df Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Thu, 28 May 2026 20:20:55 +0200
+Subject: rose: drop CALL_REQUEST in loopback timer when device is not running
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit cf5567a2652e44866eae8987dff4c1ea507680df upstream.
+
+When ax25stop brings down rose0 while the loopback timer has pending
+CALL_REQUEST frames, rose_loopback_timer() calls rose_dev_get() and
+finds the device still registered (unregister_netdevice waits for
+refs to drop), then calls rose_rx_call_request() which takes a
+netdev_hold() for the new socket.
+
+But NETDEV_DOWN fires only once: rose_kill_by_device() already ran
+before this timer tick, so the new socket is never cleaned up.  The
+stuck reference prevents unregister_netdevice from completing, and the
+orphan socket's timers eventually fire on freed memory (KASAN
+slab-use-after-free in __run_timers).
+
+The kernel clears IFF_UP via dev_close() before sending NETDEV_DOWN,
+so checking netif_running() after rose_dev_get() is sufficient: if the
+device is no longer running, the CALL_REQUEST is silently dropped and
+no socket is created.  This closes the race without touching the
+module-exit path (which already stops the timer via loopback_stopping).
+
+Tested: unregister_netdevice completes immediately after ax25stop with
+active loopback connections; no ref_tracker warnings, no KASAN.
+
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/rose_loopback.c |   10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/net/rose/rose_loopback.c
++++ b/net/rose/rose_loopback.c
+@@ -118,6 +118,16 @@ static void rose_loopback_timer(struct t
+                               kfree_skb(skb);
+                               continue;
+                       }
++                      /* rose_kill_by_device() runs on NETDEV_DOWN (IFF_UP cleared)
++                       * before the device is unregistered.  If we create a new
++                       * socket here after that cleanup, the ref never gets released
++                       * because NETDEV_DOWN fires only once.  Drop the call instead.
++                       */
++                      if (!netif_running(dev)) {
++                              dev_put(dev);
++                              kfree_skb(skb);
++                              continue;
++                      }
+                       if (rose_rx_call_request(skb, dev, rose_loopback_neigh, lci_o) == 0)
+                               kfree_skb(skb);
diff --git a/queue-7.0/rose-fix-dev_put-leak-in-rose_loopback_timer.patch b/queue-7.0/rose-fix-dev_put-leak-in-rose_loopback_timer.patch
new file mode 100644 (file)
index 0000000..ef529f7
--- /dev/null
@@ -0,0 +1,56 @@
+From ff91adc54db2b62c7cdf063ff761eceb5adf2215 Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Sat, 16 May 2026 12:09:33 +0200
+Subject: rose: fix dev_put() leak in rose_loopback_timer()
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit ff91adc54db2b62c7cdf063ff761eceb5adf2215 upstream.
+
+rose_rx_call_request() always consumes or returns the skb but never
+releases the device reference obtained from rose_dev_get().  When
+rose_rx_call_request() succeeds (returns non-zero) dev_put() was never
+called, leaking one reference per loopback CALL_REQUEST.
+
+Move dev_put() outside the conditional so it is called unconditionally
+after rose_rx_call_request() in all cases.
+
+Also remove the dead check (!rose_loopback_neigh->dev &&
+!rose_loopback_neigh->loopback) that immediately precedes it: the
+loopback neighbour always has loopback=1 so this condition can never
+be true.
+
+Fixes: 0453c6824595 ("net/rose: fix unbound loop in rose_loopback_timer()")
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/rose_loopback.c |   11 ++---------
+ 1 file changed, 2 insertions(+), 9 deletions(-)
+
+--- a/net/rose/rose_loopback.c
++++ b/net/rose/rose_loopback.c
+@@ -96,22 +96,15 @@ static void rose_loopback_timer(struct t
+               }
+               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);
+               }
diff --git a/queue-7.0/rose-fix-netdev-double-hold-in-rose_make_new.patch b/queue-7.0/rose-fix-netdev-double-hold-in-rose_make_new.patch
new file mode 100644 (file)
index 0000000..ad1c82f
--- /dev/null
@@ -0,0 +1,50 @@
+From b9fb21ceb4f0d043767a1eba60786ec84809033b Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Thu, 28 May 2026 19:11:55 +0200
+Subject: rose: fix netdev double-hold in rose_make_new()
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit b9fb21ceb4f0d043767a1eba60786ec84809033b upstream.
+
+rose_make_new() copies orose->device from the listener socket and calls
+netdev_hold(), storing the tracker in rose->dev_tracker.  The only
+caller, rose_rx_call_request(), then overwrites both make_rose->device
+and make_rose->dev_tracker with a fresh netdev_hold() for the actual
+incoming-call device.
+
+This orphans the tracker allocated by rose_make_new(): it remains in
+the device's refcount_tracker list but no pointer exists to free it
+via netdev_put().  The result is one spurious outstanding reference per
+accepted CALL_REQUEST, visible at rmmod time as:
+
+  ref_tracker: netdev@X has 2/2 users at
+      rose_rx_call_request+0xba3/0x1d50 [rose]
+      rose_loopback_timer+0x3eb/0x670 [rose]
+
+The second entry is the orphaned tracker from rose_make_new(); the
+first is the correctly-managed socket reference from rose_rx_call_request().
+
+Fix: initialise rose->device to NULL in rose_make_new() and let
+rose_rx_call_request() -- the sole caller -- assign the correct device
+and take the sole netdev_hold() as it already does.
+
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/af_rose.c |    4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/net/rose/af_rose.c
++++ b/net/rose/af_rose.c
+@@ -631,9 +631,7 @@ static struct sock *rose_make_new(struct
+       rose->hb        = orose->hb;
+       rose->idle      = orose->idle;
+       rose->defer     = orose->defer;
+-      rose->device    = orose->device;
+-      if (rose->device)
+-              netdev_hold(rose->device, &rose->dev_tracker, GFP_ATOMIC);
++      rose->device    = NULL;  /* rose_rx_call_request() sets this */
+       rose->qbitincl  = orose->qbitincl;
+       return sk;
diff --git a/queue-7.0/rose-fix-netdev-double-hold-in-rose_rx_call_request.patch b/queue-7.0/rose-fix-netdev-double-hold-in-rose_rx_call_request.patch
new file mode 100644 (file)
index 0000000..1760a3a
--- /dev/null
@@ -0,0 +1,49 @@
+From c675277c3ba0d2310e0825577d58308c39931e14 Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Tue, 26 May 2026 15:57:04 +0200
+Subject: rose: fix netdev double-hold in rose_rx_call_request()
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit c675277c3ba0d2310e0825577d58308c39931e14 upstream.
+
+rose_rx_call_request() used netdev_tracker_alloc() after assigning
+make_rose->device, intending to take ownership of the reference passed
+by the caller.  But every caller -- rose_route_frame() and
+rose_loopback_timer() -- already calls dev_put() for its own hold after
+the function returns, so the socket ended up with a tracker entry
+pointing at a reference that had already been released.
+
+The result was spurious refcount_t warnings ("saturated", "decrement
+hit 0") on every incoming CALL_REQUEST, leading to refcount corruption
+and eventual silent freeze.
+
+Replace netdev_tracker_alloc() with netdev_hold() so that
+rose_rx_call_request() acquires its own independent reference.  Each
+caller retains its own hold from rose_dev_get() and releases it via
+dev_put() as before; socket cleanup releases the socket's separate hold
+via netdev_put().
+
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/af_rose.c |    8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+--- a/net/rose/af_rose.c
++++ b/net/rose/af_rose.c
+@@ -1079,9 +1079,11 @@ int rose_rx_call_request(struct sk_buff
+               make_rose->source_digis[n] = facilities.source_digis[n];
+       make_rose->neighbour     = neigh;
+       make_rose->device        = dev;
+-      /* Caller got a reference for us. */
+-      netdev_tracker_alloc(make_rose->device, &make_rose->dev_tracker,
+-                           GFP_ATOMIC);
++      /* Take an independent reference for this socket; callers keep their
++       * own reference (from rose_dev_get / dev_hold) and will release it
++       * themselves via dev_put().
++       */
++      netdev_hold(make_rose->device, &make_rose->dev_tracker, GFP_ATOMIC);
+       make_rose->facilities    = facilities;
+       rose_neigh_hold(make_rose->neighbour);
diff --git a/queue-7.0/rose-fix-notifier-unregistered-too-early-in-rose_exit.patch b/queue-7.0/rose-fix-notifier-unregistered-too-early-in-rose_exit.patch
new file mode 100644 (file)
index 0000000..dc85707
--- /dev/null
@@ -0,0 +1,67 @@
+From f71a8a1edc14dba746edde38adddd654ba202b4d Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Tue, 26 May 2026 15:57:47 +0200
+Subject: rose: fix notifier unregistered too early in rose_exit()
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit f71a8a1edc14dba746edde38adddd654ba202b4d upstream.
+
+rose_exit() called unregister_netdevice_notifier() before the loop that
+calls unregister_netdev() on each ROSE virtual device.  As a result,
+the NETDEV_DOWN event fired by unregister_netdev() was never delivered
+to rose_device_event(), so rose_kill_by_device() never ran.
+
+Every socket whose rose->device pointed at a ROSE device therefore kept
+its netdev_tracker entry live until free_netdev() destroyed the
+ref_tracker_dir, at which point the kernel reported all of them as
+leaked references (165 entries in a typical FPAC setup).  Worse, those
+sockets retained stale device pointers and live timers that could fire
+into freed module text after module unload, causing a silent system
+freeze with no kernel panic logged.
+
+Fix by moving unregister_netdevice_notifier() to after the device-
+unregistration loop.  unregister_netdev() then delivers NETDEV_DOWN
+while the notifier is still registered, rose_kill_by_device() runs for
+each device, releases all netdev references held by open sockets, and
+calls rose_disconnect() which stops the per-socket timers.
+
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/af_rose.c |   13 +++++++++++--
+ 1 file changed, 11 insertions(+), 2 deletions(-)
+
+--- a/net/rose/af_rose.c
++++ b/net/rose/af_rose.c
+@@ -1669,19 +1669,28 @@ static void __exit rose_exit(void)
+ #ifdef CONFIG_SYSCTL
+       rose_unregister_sysctl();
+ #endif
+-      unregister_netdevice_notifier(&rose_dev_notifier);
+-
+       sock_unregister(PF_ROSE);
+       for (i = 0; i < rose_ndevs; i++) {
+               struct net_device *dev = dev_rose[i];
+               if (dev) {
++                      /* unregister_netdev() fires NETDEV_DOWN, which -- while the
++                       * notifier is still registered below -- invokes
++                       * rose_kill_by_device(dev).  That releases every socket's
++                       * netdev reference and disconnects all active circuits.
++                       * Unregistering the notifier before this loop was the
++                       * original bug: NETDEV_DOWN was never delivered, leaving
++                       * 165 netdev_tracker entries leaked and stale timers live.
++                       */
+                       unregister_netdev(dev);
+                       free_netdev(dev);
+               }
+       }
++      /* Now safe to remove the notifier -- all ROSE devices are gone. */
++      unregister_netdevice_notifier(&rose_dev_notifier);
++
+       kfree(dev_rose);
+       proto_unregister(&rose_proto);
+ }
diff --git a/queue-7.0/rose-fix-race-between-loopback-timer-and-module-removal.patch b/queue-7.0/rose-fix-race-between-loopback-timer-and-module-removal.patch
new file mode 100644 (file)
index 0000000..8c2069c
--- /dev/null
@@ -0,0 +1,108 @@
+From 47dd6ec1a77d77895afb00aa2e68373a48289108 Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Sat, 16 May 2026 12:10:20 +0200
+Subject: rose: fix race between loopback timer and module removal
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit 47dd6ec1a77d77895afb00aa2e68373a48289108 upstream.
+
+rose_loopback_clear() called timer_delete() which returns immediately
+without waiting for any running callback to complete.  If the timer
+fired concurrently with module removal, rose_loopback_timer() could
+re-arm the timer after timer_delete() returned and then access
+rose_loopback_neigh after it was freed.
+
+Two complementary changes close the race:
+
+1. Add a loopback_stopping atomic flag.  rose_loopback_timer() checks
+   it at entry (before acquiring a reference) and again inside the
+   loop; when set it drains the queue and exits without re-arming the
+   timer.
+
+2. Switch rose_loopback_clear() to timer_delete_sync() so it blocks
+   until any in-flight callback has returned before freeing resources.
+
+The smp_mb() between setting the flag and calling timer_delete_sync()
+ensures the flag is visible to any callback that is about to run.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/rose_loopback.c |   31 ++++++++++++++++++++++++-------
+ 1 file changed, 24 insertions(+), 7 deletions(-)
+
+--- 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,6 +68,9 @@ static void rose_loopback_timer(struct t
+       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
+@@ -75,6 +80,13 @@ static void rose_loopback_timer(struct t
+               skb = skb_dequeue(&loopback_queue);
+               if (!skb)
+                       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;
+@@ -118,7 +130,7 @@ static void rose_loopback_timer(struct t
+ out:
+       rose_neigh_put(rose_loopback_neigh);
+-      if (!skb_queue_empty(&loopback_queue))
++      if (!atomic_read(&loopback_stopping) && !skb_queue_empty(&loopback_queue))
+               mod_timer(&loopback_timer, jiffies + 1);
+ }
+@@ -126,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();
++
++      timer_delete_sync(&loopback_timer);
+-      while ((skb = skb_dequeue(&loopback_queue)) != NULL) {
+-              skb->sk = NULL;
++      while ((skb = skb_dequeue(&loopback_queue)) != NULL)
+               kfree_skb(skb);
+-      }
+ }
diff --git a/queue-7.0/rose-guard-rose_neigh_put-against-null-in-timer-expiry.patch b/queue-7.0/rose-guard-rose_neigh_put-against-null-in-timer-expiry.patch
new file mode 100644 (file)
index 0000000..8506dca
--- /dev/null
@@ -0,0 +1,41 @@
+From 2b67342c6ff899a0b83359517146a5b7b243af97 Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Sat, 16 May 2026 12:10:55 +0200
+Subject: rose: guard rose_neigh_put() against NULL in timer expiry
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit 2b67342c6ff899a0b83359517146a5b7b243af97 upstream.
+
+In rose_timer_expiry(), the ROSE_STATE_2 branch calls
+rose_neigh_put(rose->neighbour) without first checking whether the
+pointer is NULL.  After commit 5de7665e0a07 ("net: rose: fix timer
+races against user threads") the timer is re-armed when the socket is
+owned by a user thread; between the re-arm and the next firing, a
+device-down event or concurrent teardown via rose_kill_by_device() can
+set rose->neighbour to NULL, leading to a NULL-pointer dereference
+inside rose_neigh_put().
+
+Add a NULL check before the put and clear the pointer afterwards.
+
+Fixes: 5de7665e0a07 ("net: rose: fix timer races against user threads")
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/rose_timer.c |    5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/net/rose/rose_timer.c
++++ b/net/rose/rose_timer.c
+@@ -180,7 +180,10 @@ static void rose_timer_expiry(struct tim
+               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;
diff --git a/queue-7.0/rose-hold-loopback-neighbour-reference-across-timer-callback.patch b/queue-7.0/rose-hold-loopback-neighbour-reference-across-timer-callback.patch
new file mode 100644 (file)
index 0000000..02173f5
--- /dev/null
@@ -0,0 +1,56 @@
+From d270a7a5793af84555c40dd1eb80f1d497fdf53c Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Sat, 16 May 2026 12:10:03 +0200
+Subject: rose: hold loopback neighbour reference across timer callback
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit d270a7a5793af84555c40dd1eb80f1d497fdf53c upstream.
+
+rose_loopback_timer() dereferences rose_loopback_neigh throughout its
+body but holds no reference on it.  A concurrent rose_loopback_clear()
+followed by rose_add_loopback_neigh() could free and reallocate the
+neighbour while the timer body is running, causing a use-after-free.
+
+Take a reference with rose_neigh_hold() at the start of the callback
+(bailing out if the pointer is already NULL) and release it with
+rose_neigh_put() at the single exit point.  The neigh cannot be freed
+while the callback holds a reference.
+
+Fixes: d860d1faa6b2 ("net: rose: convert 'use' field to refcount_t")
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/rose_loopback.c |   11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+--- a/net/rose/rose_loopback.c
++++ b/net/rose/rose_loopback.c
+@@ -66,10 +66,15 @@ static void rose_loopback_timer(struct t
+       unsigned int lci_i, lci_o;
+       int count;
++      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 (skb->len < ROSE_MIN_LEN) {
+                       kfree_skb(skb);
+                       continue;
+@@ -109,6 +114,10 @@ static void rose_loopback_timer(struct t
+                       kfree_skb(skb);
+               }
+       }
++
++out:
++      rose_neigh_put(rose_loopback_neigh);
++
+       if (!skb_queue_empty(&loopback_queue))
+               mod_timer(&loopback_timer, jiffies + 1);
+ }
diff --git a/queue-7.0/rose-release-netdev-ref-and-destroy-orphaned-incoming-sockets.patch b/queue-7.0/rose-release-netdev-ref-and-destroy-orphaned-incoming-sockets.patch
new file mode 100644 (file)
index 0000000..c3984e0
--- /dev/null
@@ -0,0 +1,81 @@
+From df12be096302d2c947388acc25764456c7f18cc1 Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Thu, 28 May 2026 19:38:31 +0200
+Subject: rose: release netdev ref and destroy orphaned incoming sockets
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit df12be096302d2c947388acc25764456c7f18cc1 upstream.
+
+Two related cleanup gaps left the module unremovable after a loopback
+session:
+
+1. rose_destroy_socket() did not release the device reference.  When
+   an unaccepted incoming socket (created by rose_rx_call_request()) is
+   destroyed via rose_heartbeat_expiry(), it is removed from rose_list
+   before rose_kill_by_device() can find it, so the netdev_hold() taken
+   in rose_rx_call_request() was never matched by netdev_put().  Add the
+   release at the top of rose_destroy_socket() guarded by a NULL check
+   so that rose_release() and rose_kill_by_device(), which already call
+   netdev_put() and set device = NULL, are not affected.
+
+2. rose_heartbeat_expiry() STATE_0 cleanup required TCP_LISTEN in
+   addition to SOCK_DEAD.  Unaccepted incoming sockets are
+   TCP_ESTABLISHED, so the condition was never true and those sockets
+   lingered forever, holding the module use count above zero and
+   blocking rmmod.  Drop the TCP_LISTEN restriction: any STATE_0 +
+   SOCK_DEAD socket is orphaned and should be destroyed.
+
+Together with the earlier rose_make_new() double-hold fix these three
+patches allow clean rmmod after loopback sessions.
+
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/af_rose.c    |    9 +++++++++
+ net/rose/rose_timer.c |    9 +++++----
+ 2 files changed, 14 insertions(+), 4 deletions(-)
+
+--- a/net/rose/af_rose.c
++++ b/net/rose/af_rose.c
+@@ -363,6 +363,7 @@ static void rose_destroy_timer(struct ti
+  */
+ void rose_destroy_socket(struct sock *sk)
+ {
++      struct rose_sock *rose = rose_sk(sk);
+       struct sk_buff *skb;
+       rose_remove_socket(sk);
+@@ -370,6 +371,14 @@ void rose_destroy_socket(struct sock *sk
+       rose_stop_idletimer(sk);
+       rose_stop_timer(sk);
++      /* Drop any device reference not already released by rose_kill_by_device()
++       * or rose_release() -- e.g. incoming sockets that were never accepted.
++       */
++      if (rose->device) {
++              netdev_put(rose->device, &rose->dev_tracker);
++              rose->device = NULL;
++      }
++
+       rose_clear_queues(sk);          /* Flush the queues */
+       while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
+--- a/net/rose/rose_timer.c
++++ b/net/rose/rose_timer.c
+@@ -128,10 +128,11 @@ static void rose_heartbeat_expiry(struct
+       }
+       switch (rose->state) {
+       case ROSE_STATE_0:
+-              /* Magic here: If we listen() and a new link dies before it
+-                 is accepted() it isn't 'dead' so doesn't get removed. */
+-              if (sock_flag(sk, SOCK_DESTROY) ||
+-                  (sk->sk_state == TCP_LISTEN && sock_flag(sk, SOCK_DEAD))) {
++              /* Destroy any orphaned STATE_0 socket: either explicitly
++               * flagged SOCK_DESTROY, or SOCK_DEAD (covers both unaccepted
++               * incoming connections and listening sockets whose link died).
++               */
++              if (sock_flag(sk, SOCK_DESTROY) || sock_flag(sk, SOCK_DEAD)) {
+                       bh_unlock_sock(sk);
+                       rose_destroy_socket(sk);
+                       sock_put(sk);
diff --git a/queue-7.0/rose-set-sock_destroy-in-rose_kill_by_device-for-prompt-cleanup.patch b/queue-7.0/rose-set-sock_destroy-in-rose_kill_by_device-for-prompt-cleanup.patch
new file mode 100644 (file)
index 0000000..e9321b6
--- /dev/null
@@ -0,0 +1,43 @@
+From 741a4863ad570889c75f7a8e404567d8f3e46335 Mon Sep 17 00:00:00 2001
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Date: Wed, 27 May 2026 14:11:21 +0200
+Subject: rose: set SOCK_DESTROY in rose_kill_by_device() for prompt cleanup
+
+From: Bernard Pidoux <bernard.f6bvp@gmail.com>
+
+commit 741a4863ad570889c75f7a8e404567d8f3e46335 upstream.
+
+When rose_kill_by_device() is called (via NETDEV_DOWN on module exit
+or interface removal), it calls rose_disconnect() which transitions
+sockets to ROSE_STATE_0 and sets SOCK_DEAD.  However,
+rose_heartbeat_expiry() only calls rose_destroy_socket() at
+ROSE_STATE_0 if SOCK_DESTROY is set -- the SOCK_DEAD path is reserved
+for TCP_LISTEN sockets.  Without SOCK_DESTROY, orphaned sockets in
+ROSE_STATE_2 (clearing) loop indefinitely in the heartbeat without
+ever being freed, keeping the module use-count elevated and blocking
+modprobe -r rose until the T1 timer (up to 200 s) expires.
+
+Set SOCK_DESTROY immediately after rose_disconnect() so the heartbeat
+destroys the socket at its next tick (within 5 s), allowing clean
+module unload.
+
+Signed-off-by: Bernard Pidoux <bernard.f6bvp@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/rose/af_rose.c |    5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/net/rose/af_rose.c
++++ b/net/rose/af_rose.c
+@@ -211,6 +211,11 @@ start:
+               spin_lock_bh(&rose_list_lock);
+               if (rose->device == dev) {
+                       rose_disconnect(sk, ENETUNREACH, ROSE_OUT_OF_ORDER, 0);
++                      /* Mark for destruction so rose_heartbeat_expiry()
++                       * cleans up the socket at its next tick rather than
++                       * looping forever in ROSE_STATE_0 with no owner.
++                       */
++                      sock_set_flag(sk, SOCK_DESTROY);
+                       if (rose->neighbour)
+                               rose_neigh_put(rose->neighbour);
+                       netdev_put(rose->device, &rose->dev_tracker);
index 676eaf89dd598c53a04b8a0b16415b25e03e15ec..16572c3f61058cb5e9953c704f1d969eda72806b 100644 (file)
@@ -13,3 +13,18 @@ bpf-fix-null-pointer-dereference-in-bpf_sk_storage_clone-and-diag-paths.patch
 i2c-stub-reject-i2c-block-transfers-with-invalid-length.patch
 net-qualcomm-rmnet-fix-endpoint-use-after-free-in-rmnet_dellink.patch
 agp-amd64-fix-broken-error-propagation-in-agp_amd64_probe.patch
+rose-fix-dev_put-leak-in-rose_loopback_timer.patch
+rose-hold-loopback-neighbour-reference-across-timer-callback.patch
+rose-fix-race-between-loopback-timer-and-module-removal.patch
+rose-clear-neighbour-pointer-after-rose_neigh_put-in-state-machines.patch
+rose-guard-rose_neigh_put-against-null-in-timer-expiry.patch
+rose-fix-netdev-double-hold-in-rose_rx_call_request.patch
+rose-fix-notifier-unregistered-too-early-in-rose_exit.patch
+rose-set-sock_destroy-in-rose_kill_by_device-for-prompt-cleanup.patch
+rose-disconnect-orphaned-state_2-sockets-when-device-is-gone.patch
+rose-fix-netdev-double-hold-in-rose_make_new.patch
+rose-release-netdev-ref-and-destroy-orphaned-incoming-sockets.patch
+rose-drop-call_request-in-loopback-timer-when-device-is-not-running.patch
+rose-cancel-neighbour-timers-in-rose_neigh_put-before-freeing.patch
+rose-clear-neighbour-pointer-in-rose_kill_by_device.patch
+rose-don-t-free-fd-owned-sockets-when-reaping-in-the-heartbeat.patch