Re: [PATCH v2 4/7] s390/pci: Fix use-after-free race in zpci floating interrupt cleanup

From: Niklas Schnelle

Date: Tue Oct 06 2026 - 15:24:57 EST


On Mon, 2026-10-05 at 14:03 +0200, Tobias Schumacher wrote:
> zpci_clear_irq() stops the adapter from raising new interrupts for the
> function, but a zpci_floating_irq_handler() already running on another CPU
> can still be scanning zdev->aibv when zpci_msi_teardown_floating() releases
> it.
>
> Clear the zpci_ibv[] entry so no further handler picks the vector up, then
> wait for a grace period before releasing it. The handler runs inside the
> rcu_read_lock() section that do_airq_interrupt() holds across
> airq->handler(), so synchronize_rcu() drains any handler still in flight.
> Free the summary bit only after the grace period, so it cannot be handed to
> another device while a reader still holds the old pointer.
>
> zpci_ibv served both delivery modes, indexed by summary bit under
> FLOATING and by cpu under DIRECTED. Only the floating vectors are
> published to and torn down under the interrupt handler, so split the
> directed vectors out into zpci_dibv and annotate zpci_ibv __rcu, which
> lets sparse check the accessors above.
>
> Fixes: f770950a4709 ("s390/pci: Migrate s390 IRQ logic to IRQ domain API")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Tobias Schumacher <ts@xxxxxxxxxxxxx>
> ---
> arch/s390/pci/pci_irq.c | 60 +++++++++++++++++++++++++++----------------------
> 1 file changed, 33 insertions(+), 27 deletions(-)
>
--- snip ---
> @@ -660,10 +664,12 @@ void __init zpci_irq_exit(void)
>
> if (irq_delivery == DIRECTED) {
> for_each_possible_cpu(cpu) {
> - airq_iv_release(zpci_ibv[cpu]);
> + airq_iv_release(zpci_dibv[cpu]);
> }
> + kfree(zpci_dibv);
> + } else {
> + kfree(zpci_ibv);
> }
> - kfree(zpci_ibv);
> if (zpci_sbv)
> airq_iv_release(zpci_sbv);
> unregister_adapter_interrupt(&zpci_airq);

This is pre-existing but my additional LLM review noticed it. It
seems like a theoretical race and against the reverse cleanup vs setup
rule, that kfree(zpci_ibv) is done before
unregister_adapter_interrupt().

Thanks,
Niklas