In the Linux kernel, the following vulnerability has been resolved:
dm thin: Fix UAF in runtimersoftirq()
When dmresume() and dmdestroy() are concurrent, it will lead to UAF, as follows:
BUG: KASAN: use-after-free in _runtimers+0x173/0x710 Write of size 8 at addr ffff88816d9490f0 by task swapper/0/0 <snip> Call Trace: <IRQ> dumpstacklvl+0x73/0x9f printreport.cold+0x132/0xaa2 _rawspinlockirqsave+0xcd/0x160 _runtimers+0x173/0x710 kasanreport+0xad/0x110 _runtimers+0x173/0x710 _asanstore8+0x9c/0x140 _runtimers+0x173/0x710 calltimerfn+0x310/0x310 pvclockclocksourceread+0xfa/0x250 kvmclockread+0x2c/0x70 kvmclockgetcycles+0xd/0x20 ktimeget+0x5c/0x110 lapicnextevent+0x38/0x50 clockeventsprogramevent+0xf1/0x1e0 runtimersoftirq+0x49/0x90 _dosoftirq+0x16e/0x62c _irqexitrcu+0x1fa/0x270 irqexitrcu+0x12/0x20 sysvecapictimer_interrupt+0x8e/0xc0
One of the concurrency UAF can be shown as below:
use free
doresume | _finddevicehashcell | dmget | atomicinc(&md->holders) | | dmdestroy | _dmdestroy | if (!dmsuspendedmd(md)) | atomicread(&md->holders) | msleep(1) dmresume | _dmresume | dmtableresumetargets | poolresume | dowaker #add delay work | dmput | atomicdec(&md->holders) | | dmtabledestroy | pooldtr | _pooldec | _pooldestroy | destroyworkqueue | kfree(pool) # free pool time out _dosoftirq runtimer_softirq # pool has already been freed
This can be easily reproduced using: 1. create thin-pool 2. dmsetup suspend pool 3. dmsetup resume pool 4. dmsetup remove_all # Concurrent with 3
The root cause of this UAF bug is that dmresume() adds timer after dmdestroy() skips cancelling the timer because of suspend status. After timeout, it will call runtimersoftirq(), however pool has already been freed. The concurrency UAF bug will happen.
Therefore, cancelling timer again in _pooldestroy().