In the Linux kernel, the following vulnerability has been resolved:
nfc: nci: add flush_workqueue to prevent uaf
Our detector found a concurrent use-after-free bug when detaching an NCI device. The main reason for this bug is the unexpected scheduling between the used delayed mechanism (timer and workqueue).
The race can be demonstrated below:
Thread-1 Thread-2 | ncidevup() | nciopendevice() | _ncirequest(nciresetreq) | ncisendcmd | queuework(cmdwork) nciunregisterdevice() | nciclosedevice() | ... deltimersync(cmdtimer)[1] | ... | Worker ncifreedevice() | ncicmdwork() kfree(ndev)[3] | modtimer(cmd_timer)[2]
In short, the cleanup routine thought that the cmdtimer has already been detached by [1] but the modtimer can re-attach the timer [2], even it is already released [3], resulting in UAF.
This UAF is easy to trigger, crash trace by POC is like below
[ 66.703713] ================================================================== [ 66.703974] BUG: KASAN: use-after-free in enqueuetimer+0x448/0x490 [ 66.703974] Write of size 8 at addr ffff888009fb7058 by task kworker/u4:1/33 [ 66.703974] [ 66.703974] CPU: 1 PID: 33 Comm: kworker/u4:1 Not tainted 5.18.0-rc2 #5 [ 66.703974] Workqueue: nfc2ncicmdwq ncicmdwork [ 66.703974] Call Trace: [ 66.703974] <TASK> [ 66.703974] dumpstacklvl+0x57/0x7d [ 66.703974] printreport.cold+0x5e/0x5db [ 66.703974] ? enqueuetimer+0x448/0x490 [ 66.703974] kasanreport+0xbe/0x1c0 [ 66.703974] ? enqueuetimer+0x448/0x490 [ 66.703974] enqueuetimer+0x448/0x490 [ 66.703974] _modtimer+0x5e6/0xb80 [ 66.703974] ? markheldlocks+0x9e/0xe0 [ 66.703974] ? trytodeltimersync+0xf0/0xf0 [ 66.703974] ? lockdephardirqsonprepare+0x17b/0x410 [ 66.703974] ? queueworkon+0x61/0x80 [ 66.703974] ? lockdephardirqson+0xbf/0x130 [ 66.703974] processonework+0x8bb/0x1510 [ 66.703974] ? lockdephardirqsonprepare+0x410/0x410 [ 66.703974] ? pwqdecnrinflight+0x230/0x230 [ 66.703974] ? rwlockbug.part.0+0x90/0x90 [ 66.703974] ? rawspinlockirq+0x41/0x50 [ 66.703974] workerthread+0x575/0x1190 [ 66.703974] ? processonework+0x1510/0x1510 [ 66.703974] kthread+0x2a0/0x340 [ 66.703974] ? kthreadcompleteandexit+0x20/0x20 [ 66.703974] retfromfork+0x22/0x30 [ 66.703974] </TASK> [ 66.703974] [ 66.703974] Allocated by task 267: [ 66.703974] kasansavestack+0x1e/0x40 [ 66.703974] _kasankmalloc+0x81/0xa0 [ 66.703974] nciallocatedevice+0xd3/0x390 [ 66.703974] nfcmrvlnciregisterdev+0x183/0x2c0 [ 66.703974] nfcmrvlnciuartopen+0xf2/0x1dd [ 66.703974] nciuartttyioctl+0x2c3/0x4a0 [ 66.703974] ttyioctl+0x764/0x1310 [ 66.703974] _x64sysioctl+0x122/0x190 [ 66.703974] dosyscall64+0x3b/0x90 [ 66.703974] entrySYSCALL64afterhwframe+0x44/0xae [ 66.703974] [ 66.703974] Freed by task 406: [ 66.703974] kasansavestack+0x1e/0x40 [ 66.703974] kasansettrack+0x21/0x30 [ 66.703974] kasansetfreeinfo+0x20/0x30 [ 66.703974] _kasanslabfree+0x108/0x170 [ 66.703974] kfree+0xb0/0x330 [ 66.703974] nfcmrvlnciunregisterdev+0x90/0xd0 [ 66.703974] nciuartttyclose+0xdf/0x180 [ 66.703974] ttyldisckill+0x73/0x110 [ 66.703974] ttyldischangup+0x281/0x5b0 [ 66.703974] _ttyhangup.part.0+0x431/0x890 [ 66.703974] ttyrelease+0x3a8/0xc80 [ 66.703974] _fput+0x1f0/0x8c0 [ 66.703974] taskworkrun+0xc9/0x170 [ 66.703974] exittousermodeprepare+0x194/0x1a0 [ 66.703974] syscallexittousermode+0x19/0x50 [ 66.703974] dosyscall64+0x48/0x90 [ 66.703974] entrySYSCALL64after_hwframe+0x44/0x ---truncated---