In the Linux kernel, the following vulnerability has been resolved:
raid1: fix use-after-free for original bio in raid1writerequest()
r1bio->bios[] is used to record new bios that will be issued to underlying disks, however, in raid1writerequest(), r1bio->bios[] will set to the original bio temporarily. Meanwhile, if blocked rdev is set, freer1bio() will be called causing that all r1bio->bios[] to be freed:
raid1writerequest() r1bio = allocr1bio(mddev, bio); -> r1bio->bios[] is NULL for (i = 0; i < disks; i++) -> for each rdev in conf // first rdev is normal r1bio->bios[0] = bio; -> set to original bio // second rdev is blocked if (test_bit(Blocked, &rdev->flags)) break
if (blockedrdev) freer1bio() putallbios() bioput(r1bio->bios[0]) -> original bio is freed
Test scripts:
mdadm -CR /dev/md0 -l1 -n4 /dev/sd[abcd] --assume-clean fio -filename=/dev/md0 -ioengine=libaio -rw=write -bs=4k -numjobs=1 \ -iodepth=128 -name=test -direct=1 echo blocked > /sys/block/md0/md/rd2/state
Test result:
Allocated in mempoolallocslab+0x24/0x50 age=1 cpu=1 pid=869 kmemcachealloc+0x324/0x480 mempoolallocslab+0x24/0x50 mempoolalloc+0x6e/0x220 bioallocbioset+0x1af/0x4d0 blkdevdirectIO+0x164/0x8a0 blkdevwriteiter+0x309/0x440 aiowrite+0x139/0x2f0 iosubmitone+0x5ca/0xb70 _dosysiosubmit+0x86/0x270 _x64sysiosubmit+0x22/0x30 dosyscall64+0xb1/0x210 entrySYSCALL64afterhwframe+0x6c/0x74 Freed in mempoolfreeslab+0x1f/0x30 age=1 cpu=1 pid=869 kmemcachefree+0x28c/0x550 mempoolfreeslab+0x1f/0x30 mempoolfree+0x40/0x100 biofree+0x59/0x80 bioput+0xf0/0x220 freer1bio+0x74/0xb0 raid1makerequest+0xadf/0x1150 mdhandlerequest+0xc7/0x3b0 mdsubmitbio+0x76/0x130 _submitbio+0xd8/0x1d0 submitbionoacctnocheck+0x1eb/0x5c0 submitbionoacct+0x169/0xd40 submitbio+0xee/0x1d0 blkdevdirectIO+0x322/0x8a0 blkdevwriteiter+0x309/0x440 aio_write+0x139/0x2f0
Since that bios for underlying disks are not allocated yet, fix this problem by using mempoolfree() directly to free the r1bio.