Merge branch 's390-ctcm-fix-timer-corruption-and-use-after-free'

Nagamani says:

====================
s390/ctcm: Fix timer corruption and use-after-free

Fix two bugs in drivers/s390/net/fsm.c and drivers/s390/net/ctcm_main.c
found by Sashiko AI code review.

Patch 1 fixes timer list corruption when fsm_addtimer() is called on
an already-pending timer - timer_setup() re-initializes the timer
list_head while it is still enqueued in the wheel.

Patch 2 fixes a use-after-free in channel_remove(). For MPC channels
the teardown has a circular dependency: the ch->timer callback can
schedule ch_disc_tasklet via mpc_action_go_inop(), ch_tasklet can
re-arm sweep_timer, and sweep_timer's callback re-arms ch->timer.
Fix by shutting down both MPC timers with timer_shutdown_sync() first
(sweep_timer before ch->timer), then killing both tasklets. This breaks
all three re-arm paths before freeing channel resources.
====================

Link: https://patch.msgid.link/20260930070234.569500-1-nagamani@linux.ibm.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-10-05 17:30:30 -07:00
commit 94085db4a1
2 changed files with 9 additions and 14 deletions

View File

@ -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);

View File

@ -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);