channel_remove() calls fsm_deltimer() which internally uses
timer_delete(), then immediately frees the channel structure:
fsm_deltimer(&ch->timer);
kfree_fsm(ch->fsm); /* freed while callback may still run */
kfree(ch);
timer_delete() returns immediately even if the timer callback is
currently executing on another CPU, creating a window where
fsm_expire_timer() accesses this->fi (which points to ch->fsm)
after it has been freed by kfree_fsm().
Fix this by calling timer_delete_sync() directly on the underlying
timer_list fields before freeing, instead of going through
fsm_deltimer(). timer_delete_sync() is used rather than
timer_shutdown_sync() because the channel is fully torn down by
channel_remove() and the timer is never re-armed after this point,
making the re-arm prevention of timer_shutdown_sync() unnecessary.
This cannot be fixed in fsm_deltimer() itself because FSM action
functions triggered by CTC_EVENT_TIMER call fsm_deltimer() from within
the timer callback chain, which would cause a self-deadlock in
timer_delete_sync().
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://sashiko.dev/#/patchset/20260803182736.2356374-1-nagamani@linux.ibm.com?part=1
Reviewed-by: Aswin Karuvally <aswin@linux.ibm.com>
Tested-by: Aswin Karuvally <aswin@linux.ibm.com>
Signed-off-by: Nagamani PV <redacted>
---
drivers/s390/net/ctcm_main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c
index 8b0d76a47d9f..63f8832f9b14 100644
--- a/drivers/s390/net/ctcm_main.c
+++ b/drivers/s390/net/ctcm_main.c
@@ -211,9 +211,9 @@ static void channel_remove(struct channel *ch)
while (*c) {
if (*c == ch) {
*c = ch->next;
- fsm_deltimer(&ch->timer);
+ timer_delete_sync(&ch->timer.tl);
if (IS_MPC(ch))
- fsm_deltimer(&ch->sweep_timer);
+ timer_delete_sync(&ch->sweep_timer.tl);
kfree_fsm(ch->fsm);
clear_normalized_cda(&ch->ccw[4]);--
2.53.0