summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernard Pidoux <bernard.f6bvp@gmail.com>2026-05-31 15:41:45 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-06-27 11:07:40 +0100
commit0c7919c4f1767a3402aed4118045d1f7bc72fef5 (patch)
tree1898ed44aa228df9bafa605c91937f39439d9378
parente0fbbe60d2721ae38c3e5bb4d63c50c46d4b3f08 (diff)
downloadlinux-stable-0c7919c4f1767a3402aed4118045d1f7bc72fef5.tar.gz
linux-stable-0c7919c4f1767a3402aed4118045d1f7bc72fef5.zip
rose: cancel neighbour timers in rose_neigh_put() before freeing
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>
-rw-r--r--include/net/rose.h12
1 files changed, 12 insertions, 0 deletions
diff --git a/include/net/rose.h b/include/net/rose.h
index 2b5491bbf39a..95d1f9c582dc 100644
--- a/include/net/rose.h
+++ b/include/net/rose.h
@@ -160,6 +160,18 @@ static inline void rose_neigh_hold(struct rose_neigh *rose_neigh)
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);