wifi: ath6kl: fix use-after-free in aggr_reset_state()
Subsystems (2)
PATCH 1/1 wifi: ath6kl: fix use-after-free in aggr_reset_state()
The aggr_reset_state() function uses timer_delete() (non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers like aggr_module_destroy().
If the timer callback (aggr_timeout) is executing when aggr_reset_state() is called, the callback will continue to access aggr_conn fields like rx_tid[] and stat[] which may be freed immediately after by kfree(aggr_info->aggr_conn) in aggr_module_destroy().
Additionally, the timer callback can re-arm itself via mod_timer() while aggr_reset_state() is running, creating a more complex race condition.
Use timer_delete_sync() instead to ensure any running timer callback has completed before returning.
--- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/net/wireless/ath/ath6kl/txrx.c +1 -1
@@ -1828,7 +1828,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)return ;if (aggr_conn->timer_scheduled) { -timer_delete (&aggr_conn->timer); +timer_delete_sync (&aggr_conn->timer); aggr_conn->timer_scheduled = false; }
Signature · 1 line
2.52.0
Quoted 27 lines · Daniel Hodges
The
aggr_reset_state()function usestimer_delete()(non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers likeaggr_module_destroy().If the timer callback (
aggr_timeout) is executing whenaggr_reset_state()is called, the callback will continue to accessaggr_connfields like rx_tid[] and stat[] which may be freed immediately after bykfree(aggr_info->aggr_conn)inaggr_module_destroy().Additionally, the timer callback can re-arm itself via
mod_timer()whileaggr_reset_state()is running, creating a more complex race condition.Use
timer_delete_sync()instead to ensure any running timer callback has completed before returning.Fixes bdcd81707973 ("Add ath6kl cleaned up driver")Signed-off-by Daniel Hodges <[email protected]>--- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/net/wireless/ath/ath6kl/txrx.c +0
-0
drivers/net/wireless/ath/ath6kl/txrx.c void aggr_reset_state(struct aggr_info_conn *aggr_conn) · line 1831
Quoted 5 lines
drivers/net/wireless/ath/ath6kl/txrx.c +1
-1@@ -1828,7 +1828,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)return ;if (aggr_conn->timer_scheduled) { -timer_delete (&aggr_conn->timer); +timer_delete_sync (&aggr_conn->timer);
My review agent claims this still doesn't fix the UAF since aggr_timeout() can call mod_timer() to rearm itself and hence the timer can fire again. Instead it suggests timer_shutdown_sync() should be used since that prevents any rearm from taking effect.
But I'm not familiar with this driver so I don't know if there are reasons to not use timer_shutdown_sync(), i.e. if the timer will be reused again then timer_setup() will need to be called again.
Quoted 3 lines
drivers/net/wireless/ath/ath6kl/txrx.c +0
-0aggr_conn->timer_scheduled = false; }
Quoted 39 lines · Jeff Johnson
Quoted 27 lines · Daniel Hodges
The
aggr_reset_state()function usestimer_delete()(non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers likeaggr_module_destroy().If the timer callback (
aggr_timeout) is executing whenaggr_reset_state()is called, the callback will continue to accessaggr_connfields like rx_tid[] and stat[] which may be freed immediately after bykfree(aggr_info->aggr_conn)inaggr_module_destroy().Additionally, the timer callback can re-arm itself via
mod_timer()whileaggr_reset_state()is running, creating a more complex race condition.Use
timer_delete_sync()instead to ensure any running timer callback has completed before returning.Fixes bdcd81707973 ("Add ath6kl cleaned up driver")Signed-off-by Daniel Hodges <[email protected]>--- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/net/wireless/ath/ath6kl/txrx.c +0
-0drivers/net/wireless/ath/ath6kl/txrx.c void aggr_reset_state(struct aggr_info_conn *aggr_conn) · line 1831
Quoted 5 lines
drivers/net/wireless/ath/ath6kl/txrx.c +1
-1@@ -1828,7 +1828,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)return ;if (aggr_conn->timer_scheduled) { -timer_delete (&aggr_conn->timer); +timer_delete_sync (&aggr_conn->timer);My review agent claims this still doesn't fix the UAF since
aggr_timeout()can callmod_timer()to rearm itself and hence the timer can fire again. Instead it suggeststimer_shutdown_sync()should be used since that prevents any rearm from taking effect.
Quoted 3 lines
But I'm not familiar with this driver so I don't know if there are reasons to not use
timer_shutdown_sync(), i.e. if the timer will be reused again thentimer_setup()will need to be called again.
Interesting enough, another iteration of the same agent says:
**The fix is correct.** timer_delete_sync() loops until the timer is both not-running and not-pending — it handles the re-arm case because after the callback calls mod_timer(), the sync loop picks that up and cancels it.
Gotta love our new AI-driven workflows...
/jeff
Quoted 40 lines · Jeff Johnson
Quoted 39 lines · Jeff Johnson
Quoted 27 lines · Daniel Hodges
The
aggr_reset_state()function usestimer_delete()(non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers likeaggr_module_destroy().If the timer callback (
aggr_timeout) is executing whenaggr_reset_state()is called, the callback will continue to accessaggr_connfields like rx_tid[] and stat[] which may be freed immediately after bykfree(aggr_info->aggr_conn)inaggr_module_destroy().Additionally, the timer callback can re-arm itself via
mod_timer()whileaggr_reset_state()is running, creating a more complex race condition.Use
timer_delete_sync()instead to ensure any running timer callback has completed before returning.Fixes bdcd81707973 ("Add ath6kl cleaned up driver")Signed-off-by Daniel Hodges <[email protected]>--- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/net/wireless/ath/ath6kl/txrx.c +0
-0drivers/net/wireless/ath/ath6kl/txrx.c void aggr_reset_state(struct aggr_info_conn *aggr_conn) · line 1831
Quoted 5 lines
drivers/net/wireless/ath/ath6kl/txrx.c +1
-1@@ -1828,7 +1828,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)return ;if (aggr_conn->timer_scheduled) { -timer_delete (&aggr_conn->timer); +timer_delete_sync (&aggr_conn->timer);My review agent claims this still doesn't fix the UAF since
aggr_timeout()can callmod_timer()to rearm itself and hence the timer can fire again. Instead it suggeststimer_shutdown_sync()should be used since that prevents any rearm from taking effect.
Quoted 8 lines
Quoted 3 lines
But I'm not familiar with this driver so I don't know if there are reasons to not use
timer_shutdown_sync(), i.e. if the timer will be reused again thentimer_setup()will need to be called again.Interesting enough, another iteration of the same agent says:
**The fix is correct.**timer_delete_sync()loops until the timer is both not-running and not-pending — it handles the re-arm case because after the callback callsmod_timer(), the sync loop picks that up and cancels it.
Yeah, my understanding is also that timer_delete_sync() will ensure that the timer will not be pending (including the one armed from the timer callback itself) or running when it returns with the caller ensuring the timer is not rearmed concurrently. With that, I think this code change is fine and no need to use timer_shutdown_sync().
Vasanth
Quoted 27 lines · Daniel Hodges
The
aggr_reset_state()function usestimer_delete()(non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers likeaggr_module_destroy().If the timer callback (
aggr_timeout) is executing whenaggr_reset_state()is called, the callback will continue to accessaggr_connfields like rx_tid[] and stat[] which may be freed immediately after bykfree(aggr_info->aggr_conn)inaggr_module_destroy().Additionally, the timer callback can re-arm itself via
mod_timer()whileaggr_reset_state()is running, creating a more complex race condition.Use
timer_delete_sync()instead to ensure any running timer callback has completed before returning.Fixes bdcd81707973 ("Add ath6kl cleaned up driver")Signed-off-by Daniel Hodges <[email protected]>--- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/net/wireless/ath/ath6kl/txrx.c +0
-0
drivers/net/wireless/ath/ath6kl/txrx.c void aggr_reset_state(struct aggr_info_conn *aggr_conn) · line 1834
Quoted 8 lines
drivers/net/wireless/ath/ath6kl/txrx.c +1
-1@@ -1828,7 +1828,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)return ;if (aggr_conn->timer_scheduled) { -timer_delete (&aggr_conn->timer); +timer_delete_sync (&aggr_conn->timer); aggr_conn->timer_scheduled = false; }
Quoted 27 lines · Daniel Hodges
The
aggr_reset_state()function usestimer_delete()(non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers likeaggr_module_destroy().If the timer callback (
aggr_timeout) is executing whenaggr_reset_state()is called, the callback will continue to accessaggr_connfields like rx_tid[] and stat[] which may be freed immediately after bykfree(aggr_info->aggr_conn)inaggr_module_destroy().Additionally, the timer callback can re-arm itself via
mod_timer()whileaggr_reset_state()is running, creating a more complex race condition.Use
timer_delete_sync()instead to ensure any running timer callback has completed before returning.Fixes bdcd81707973 ("Add ath6kl cleaned up driver")Signed-off-by Daniel Hodges <[email protected]>--- drivers/net/wireless/ath/ath6kl/txrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)drivers/net/wireless/ath/ath6kl/txrx.c +0
-0
drivers/net/wireless/ath/ath6kl/txrx.c void aggr_reset_state(struct aggr_info_conn *aggr_conn) · line 1834
Quoted 8 lines
drivers/net/wireless/ath/ath6kl/txrx.c +1
-1@@ -1828,7 +1828,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn)return ;if (aggr_conn->timer_scheduled) { -timer_delete (&aggr_conn->timer); +timer_delete_sync (&aggr_conn->timer); aggr_conn->timer_scheduled = false; }
I am not familiar with ath6kl either, but while looking through the code,
aggr_reset_state() still calls timer_delete_sync() only when aggr_conn->timer_scheduled is true. However aggr_timeout() clears timer_scheduled near the beginning of the callback, before it walks aggr_conn->rx_tid[] and updates aggr_conn->stat[].
So aggr_reset_state() can observe timer_scheduled == false while the
timer callback is still running, skip timer_delete_sync(), and then delete the TID state / allow aggr_conn to be freed. That still leaves the UAF window open.
I think timer_delete_sync() should be called unconditionally after the aggr_conn NULL check, and timer_scheduled can then be cleared afterwards.
--
Ramesh
Jeff Johnson
AppliedQuoted 10 lines · Daniel Hodges
The
aggr_reset_state()function usestimer_delete()(non-synchronous) for the aggregation timer before proceeding to delete TID state and before the structure is freed by callers likeaggr_module_destroy().If the timer callback (
aggr_timeout) is executing whenaggr_reset_state()is called, the callback will continue to accessaggr_connfields like rx_tid[] and stat[] which may be freed immediately after bykfree(aggr_info->aggr_conn)inaggr_module_destroy().[...]
Applied, thanks!
[1/1] wifi: ath6kl: fix use-after-free in aggr_reset_state()
commit: ba7debb4dd6427386862220e8335a53a4bfc235d
Best regards,
Signature · 1 line
Jeff Johnson <[email protected]>