diff options
| author | Bernard Pidoux <bernard.f6bvp@gmail.com> | 2026-05-31 15:41:45 +0200 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2026-06-27 11:07:40 +0100 |
| commit | 0c7919c4f1767a3402aed4118045d1f7bc72fef5 (patch) | |
| tree | 1898ed44aa228df9bafa605c91937f39439d9378 | |
| parent | e0fbbe60d2721ae38c3e5bb4d63c50c46d4b3f08 (diff) | |
| download | linux-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.h | 12 |
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); |
