From 10c339aff290684dc424ca6b6eed5579569ee781 Mon Sep 17 00:00:00 2001 From: Nagamani PV Date: Wed, 30 Sep 2026 09:02:33 +0200 Subject: [PATCH 1/2] s390/ctcm: Fix timer corruption in fsm_addtimer() fsm_addtimer() calls timer_setup() unconditionally before add_timer(). If called on an already-pending timer, timer_setup() re-initializes the timer's list_head fields while the timer is still enqueued in the wheel, corrupting the timer list. The timer is already initialized once by fsm_settimer() which calls timer_setup() correctly. Multiple callsites invoke fsm_addtimer() without a preceding fsm_deltimer(), including ctcm_main.c ctcmpc_send_sweep_req() and ctcm_mpc.c mpc_action_side_xid(), making the redundant timer_setup() in fsm_addtimer() a real corruption risk. Remove the redundant timer_setup() calls from fsm_addtimer() and fsm_modtimer(), and replace add_timer() with mod_timer() which safely handles both pending and non-pending timers atomically without corrupting the timer wheel. Cc: stable+noautosel@kernel.org # never worked Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260803182736.2356374-1-nagamani@linux.ibm.com?part=1 Reviewed-by: Aswin Karuvally Tested-by: Aswin Karuvally Signed-off-by: Nagamani PV Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260930070234.569500-2-nagamani@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/s390/net/fsm.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/s390/net/fsm.c b/drivers/s390/net/fsm.c index 9b8622aef807..936ed0bc8401 100644 --- a/drivers/s390/net/fsm.c +++ b/drivers/s390/net/fsm.c @@ -171,11 +171,9 @@ fsm_addtimer(fsm_timer *this, int millisec, int event, void *arg) this->fi->name, this, millisec); #endif - timer_setup(&this->tl, fsm_expire_timer, 0); this->expire_event = event; this->event_arg = arg; - this->tl.expires = jiffies + (millisec * HZ) / 1000; - add_timer(&this->tl); + mod_timer(&this->tl, jiffies + msecs_to_jiffies(millisec)); return 0; } @@ -189,12 +187,9 @@ fsm_modtimer(fsm_timer *this, int millisec, int event, void *arg) this->fi->name, this, millisec); #endif - timer_delete(&this->tl); - timer_setup(&this->tl, fsm_expire_timer, 0); this->expire_event = event; this->event_arg = arg; - this->tl.expires = jiffies + (millisec * HZ) / 1000; - add_timer(&this->tl); + mod_timer(&this->tl, jiffies + msecs_to_jiffies(millisec)); } EXPORT_SYMBOL(init_fsm); From 65629387fd4c0d2e7a4e6303364acfdcbcf843d9 Mon Sep 17 00:00:00 2001 From: Nagamani PV Date: Wed, 30 Sep 2026 09:02:34 +0200 Subject: [PATCH 2/2] s390/ctcm: Fix use-after-free in channel_remove() channel_remove() uses fsm_deltimer() which calls timer_delete(), returning immediately even if the timer callback is running on another CPU. This leaves a window where fsm_expire_timer() accesses ch->fsm after kfree_fsm(). For MPC channels the teardown has a circular dependency: ch->timer callback -> ctcm_chx_txretry() -> mpc_action_go_inop() -> tasklet_hi_schedule(&ch->ch_disc_tasklet) ch_tasklet -> ctcmpc_send_sweep_resp() -> fsm_addtimer(&ch->sweep_timer) sweep_timer callback -> fsm_addtimer(&ch->timer) Use timer_shutdown_sync() for both timers: it waits for any running callback and permanently prevents rearming. Shut down sweep_timer first (its callback rearms ch->timer at ctcm_fsms.c), then ch->timer. Only then call tasklet_kill() -- catching any tasklet scheduled by an in-flight callback before shutdown. kfree(ch->discontact_th) follows tasklet_kill(ch_disc_tasklet) since ch->ccw[15].cda points to discontact_th and mpc_action_send_discontact() starts I/O through ch->ccw[15]. timer_shutdown_sync() is also used for non-MPC ch->timer: ctcm_chx_txretry() (CTC_STATE_TX + CTC_EVENT_TIMER) calls fsm_addtimer(&ch->timer) on both paths. Depends on "s390/ctcm: Fix timer corruption in fsm_addtimer()" which replaces add_timer() with mod_timer(). mod_timer() on a shut-down timer is discarded; without it fsm_addtimer() calls timer_setup() defeating timer_shutdown_sync(). Backporting without that prerequisite is unsafe. Cc: stable+noautosel@kernel.org # fix for a driver (de)init path race Fixes: 293d984f0e36 ("ctcm: infrastructure for replaced ctc driver") Reported-by: Sashiko Link: https://sashiko.dev/#/patchset/20260922101913.239103-1-nagamani@linux.ibm.com?part=2 Signed-off-by: Nagamani PV Reviewed-by: Aswin Karuvally Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260930070234.569500-3-nagamani@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/s390/net/ctcm_main.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 8b0d76a47d9f..27a22dbc9280 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -211,9 +211,14 @@ static void channel_remove(struct channel *ch) while (*c) { if (*c == ch) { *c = ch->next; - fsm_deltimer(&ch->timer); if (IS_MPC(ch)) - fsm_deltimer(&ch->sweep_timer); + timer_shutdown_sync(&ch->sweep_timer.tl); + timer_shutdown_sync(&ch->timer.tl); + if (IS_MPC(ch)) { + tasklet_kill(&ch->ch_tasklet); + tasklet_kill(&ch->ch_disc_tasklet); + kfree(ch->discontact_th); + } kfree_fsm(ch->fsm); clear_normalized_cda(&ch->ccw[4]); @@ -221,11 +226,6 @@ static void channel_remove(struct channel *ch) clear_normalized_cda(&ch->ccw[1]); dev_kfree_skb_any(ch->trans_skb); } - if (IS_MPC(ch)) { - tasklet_kill(&ch->ch_tasklet); - tasklet_kill(&ch->ch_disc_tasklet); - kfree(ch->discontact_th); - } kfree(ch->ccw); kfree(ch->irb); kfree(ch);