In the Linux kernel, the following vulnerability has been resolved:
ubi: Fix race condition between ctrlcdevioctl and ubicdevioctl
Hulk Robot reported a KASAN report about use-after-free: ================================================================== BUG: KASAN: use-after-free in _listdelentryvalid+0x13d/0x160 Read of size 8 at addr ffff888035e37d98 by task ubiattach/1385 [...] Call Trace: klistdecanddel+0xa7/0x4a0 klistput+0xc7/0x1a0 devicedel+0x4d4/0xed0 cdevdevicedel+0x1a/0x80 ubiattachmtddev+0x2951/0x34b0 [ubi] ctrlcdevioctl+0x286/0x2f0 [ubi]
Allocated by task 1414: deviceadd+0x60a/0x18b0 cdevdeviceadd+0x103/0x170 ubicreatevolume+0x1118/0x1a10 [ubi] ubicdev_ioctl+0xb7f/0x1ba0 [ubi]
Freed by task 1385: cdevdevicedel+0x1a/0x80 ubiremovevolume+0x438/0x6c0 [ubi] ubicdevioctl+0xbf4/0x1ba0 [ubi] [...] ==================================================================
The lock held by ctrlcdevioctl is ubidevicesmutex, but the lock held by ubicdevioctl is ubi->device_mutex. Therefore, the two locks can be concurrent.
ctrlcdevioctl contains two operations: ubiattach and ubidetach. ubidetach is bug-free because it uses reference counting to prevent concurrency. However, uifinit and uifclose in ubiattach may race with ubicdevioctl.
uifinit will race with ubicdevioctl as in the following stack. cpu1 cpu2 cpu3 |_|_ ctrlcdevioctl ubiattachmtddev uifinit ubicdevioctl ubicreatevolume cdevdeviceadd ubiaddvolume // sysfs exist killvolumes ubicdevioctl ubiremovevolume cdevdevicedel // first free ubifreevolume cdevdel // double free cdevdevicedel
And uifclose will race with ubicdevioctl as in the following stack. cpu1 cpu2 cpu3 |_|_ ctrlcdevioctl ubiattachmtddev uifinit ubicdevioctl ubicreatevolume cdevdeviceadd ubidebugfsinitdev //error goto outuif; uifclose killvolumes ubicdevioctl ubiremovevolume cdevdevicedel // first free ubifreevolume // double free
The cause of this problem is that commit 714fb87e8bc0 make device "available" before it becomes accessible via sysfs. Therefore, we roll back the modification. We will fix the race condition between ubi device creation and udev by removing ubigetdevice in volattributeshow and devattributeshow.This avoids accessing uninitialized ubidevices[ubinum].
ubigetdevice is used to prevent devices from being deleted during sysfs execution. However, now kernfs ensures that devices will not be deleted before all reference counting are released. The key process is shown in the following stack.
devicedel deviceremoveattrs deviceremovegroups sysfsremovegroups sysfsremovegroup removefiles kernfsremovebyname kernfsremovebynamens _kernfsremove kernfsdrain