CVE-2026-97603: idpf: disable DIM work before freeing q_vectors
In the Linux kernel, the following vulnerability has been resolved:
idpf: disable DIM work before freeing qvectors
idpf never drains the Tx/Rx DIM works before freeing the memory they live in. txdim and rxdim are embedded in struct idpfqvector, they are queued from the NAPI poll via netdim(), and idpfvportintrrel() ends with kfree(rsrc->qvectors). Nothing in the driver cancels them.
idpftxdimwork() and idpfrxdimwork() then run on freed memory: idpfvportintrwriteitr() writes the ITR register through qvector->intrreg.txitr / rxitr, void iomem pointers loaded out of the freed qvector. No configuration is needed to get there -- IDPFITRISDYNAMIC() is defined as (itrmode) and idpfvportalloc() initialises both modes to IDPFITRDYNAMIC.
Draining after idpfvportintrnapidisall() is not enough on its own. idpfnetdim() is called from inside the "if (napicompletedone(napi, workdone))" branch of the poll, and napicompletedone() has already cleared NAPIFSTATESCHED by then. napidisablelocked() waits only while (val & (NAPIFSTATESCHED | NAPIFSTATENPSVC)), so napidisable() can return while the poll tail is still queueing the work, and a plain cancelworksync() would be re-armed behind the drain.
Use disableworksync(): schedulework() on a work with a non-zero disable count is dropped by clearpendingifdisabled() before queuework() is reached.
Move idpfinitdim() to idpfvportintralloc() so the works are initialised on every path that can reach the drain -- the three "goto intrdeinit" sites between idpfvportintrinit() and idpfvportintrena() get there without the enable side having run. Nothing re-enables them: rsrc->qvectors is freed on every exit from idpfvportopen() and on every idpfvportstop(), so the count dies with the object.
It is a race, not a deterministic failure -- netdim() only schedules once DIMNEVENTS events have accumulated and the profile index changes. A KASAN ifup/ifdown loop under load is the way to see it.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Compensating control
Move idpf_init_dim() to idpf_vport_intr_alloc() so DIM work is initialized before any path can reach the drain.
- Compensating control
Use disable_work_sync() for the Tx/Rx DIM work before freeing q_vectors; a plain cancel_work_sync() is insufficient because the work can requeue itself.
Event History
Frequently Asked Questions
Which systems are exposed by default?
Systems using the Linux kernel idpf driver are affected without requiring a special configuration. The driver initializes both interrupt moderation modes to IDPF_ITR_DYNAMIC, which enables the DIM work path by default.
What condition leads to the use-after-free?
Tx or Rx DIM work must be queued from the NAPI poll path and then run after the driver's q_vectors have been freed during interrupt-resource release. The delayed work can dereference the freed q_vector to access interrupt-register pointers.
Is disabling NAPI alone sufficient to prevent the race?
No. The description states that draining after NAPI disable is insufficient because the poll tail can still queue DIM work after napi_complete_done() clears the scheduled state, allowing napi_disable() to return before that work is queued.