--- /dev/null
+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);
--- /dev/null
+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;
--- /dev/null
+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;
+ }
--- /dev/null
+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.
--- /dev/null
+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);
--- /dev/null
+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);
--- /dev/null
+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);
+ }
--- /dev/null
+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;
--- /dev/null
+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);
--- /dev/null
+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);
+ }
--- /dev/null
+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);
+- }
+ }
--- /dev/null
+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;
+
--- /dev/null
+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);
+ }
--- /dev/null
+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);
--- /dev/null
+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);
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