In the Linux kernel, the following vulnerability has been resolved:
mm: zswap: fix cryptofreeacomp() deadlock in zswapcpucomp_dead()
Currently, zswapcpucompdead() calls cryptofreeacomp() while holding the per-CPU acompctx mutex. cryptofreeacomp() then holds scomplock (through cryptoexitscompops_async()).
On the other hand, cryptoallocacompnode() holds the scomplock (through cryptoscompinittfm()), and then allocates memory. If the allocation results in reclaim, we may attempt to hold the per-CPU acompctx mutex.
The above dependencies can cause an ABBA deadlock. For example in the following scenario:
(1) Task A running on CPU #1: cryptoallocacompnode() Holds scomplock Enters reclaim Reads percpuptr(pool->acomp_ctx, 1)
(2) Task A is descheduled
(3) CPU #1 goes offline zswapcpucompdead(CPU #1) Holds percpuptr(pool->acompctx, 1)) Calls cryptofreeacomp() Waits for scomp_lock
(4) Task A running on CPU #2: Waits for percpuptr(pool->acomp_ctx, 1) // Read on CPU #1 DEADLOCK
Since there is no requirement to call cryptofreeacomp() with the per-CPU acompctx mutex held in zswapcpucompdead(), move it after the mutex is unlocked. Also move the acomprequestfree() and kfree() calls for consistency and to avoid any potential sublte locking dependencies in the future.
With this, only setting acompctx fields to NULL occurs with the mutex held. This is similar to how zswapcpucompprepare() only initializes acomp_ctx fields with the mutex held, after performing all allocations before holding the mutex.
Opportunistically, move the NULL check on acomp_ctx so that it takes place before the mutex dereference.