In the Linux kernel, the following vulnerability has been resolved:
btrfs: fix a use-after-free when hitting errors inside btrfssubmitchunk()
[BUG] There is an internal report that KASAN is reporting use-after-free, with the following backtrace:
BUG: KASAN: slab-use-after-free in btrfscheckreadbio+0xa68/0xb70 [btrfs] Read of size 4 at addr ffff8881117cec28 by task kworker/u16:2/45 CPU: 1 UID: 0 PID: 45 Comm: kworker/u16:2 Not tainted 6.11.0-rc2-next-20240805-default+ #76 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.16.2-3-gd478f380-rebuilt.opensuse.org 04/01/2014 Workqueue: btrfs-endio btrfsendbiowork [btrfs] Call Trace: dumpstacklvl+0x61/0x80 printaddressdescription.constprop.0+0x5e/0x2f0 printreport+0x118/0x216 kasanreport+0x11d/0x1f0 btrfscheckreadbio+0xa68/0xb70 [btrfs] processonework+0xce0/0x12a0 workerthread+0x717/0x1250 kthread+0x2e3/0x3c0 retfromfork+0x2d/0x70 retfromfork_asm+0x11/0x20
Allocated by task 20917: kasansavestack+0x37/0x60 kasansavetrack+0x10/0x30 _kasanslaballoc+0x7d/0x80 kmemcacheallocnoprof+0x16e/0x3e0 mempoolallocnoprof+0x12e/0x310 bioallocbioset+0x3f0/0x7a0 btrfsbioalloc+0x2e/0x50 [btrfs] submitextentpage+0x4d1/0xdb0 [btrfs] btrfsdoreadpage+0x8b4/0x12a0 [btrfs] btrfsreadahead+0x29a/0x430 [btrfs] readpages+0x1a7/0xc60 pagecacheraunbounded+0x2ad/0x560 filemapgetpages+0x629/0xa20 filemapread+0x335/0xbf0 vfsread+0x790/0xcb0 ksysread+0xfd/0x1d0 dosyscall64+0x6d/0x140 entrySYSCALL64afterhwframe+0x4b/0x53
Freed by task 20917: kasansavestack+0x37/0x60 kasansavetrack+0x10/0x30 kasansavefreeinfo+0x37/0x50 _kasanslabfree+0x4b/0x60 kmemcachefree+0x214/0x5d0 biofree+0xed/0x180 endbbiodataread+0x1cc/0x580 [btrfs] btrfssubmitchunk+0x98d/0x1880 [btrfs] btrfssubmitbio+0x33/0x70 [btrfs] submitonebio+0xd4/0x130 [btrfs] submitextentpage+0x3ea/0xdb0 [btrfs] btrfsdoreadpage+0x8b4/0x12a0 [btrfs] btrfsreadahead+0x29a/0x430 [btrfs] readpages+0x1a7/0xc60 pagecacheraunbounded+0x2ad/0x560 filemapgetpages+0x629/0xa20 filemapread+0x335/0xbf0 vfsread+0x790/0xcb0 ksysread+0xfd/0x1d0 dosyscall64+0x6d/0x140 entrySYSCALL64afterhwframe+0x4b/0x53
[CAUSE] Although I cannot reproduce the error, the report itself is good enough to pin down the cause.
The call trace is the regular endio workqueue context, but the free-by-task trace is showing that during btrfssubmitchunk() we already hit a critical error, and is calling btrfsbioendio() to error out. And the original endio function called bioput() to free the whole bio.
This means a double freeing thus causing use-after-free, e.g.:
Enter btrfssubmitbio() with a read bio The read bio length is 128K, crossing two 64K stripes.
The first run of btrfssubmitchunk()
2.1 Call btrfsmapblock(), which returns 64K 2.2 Call btrfssplitbio() Now there are two bios, one referring to the first 64K, the other referring to the second 64K. 2.3 The first half is submitted.
3.1 Call btrfsmapblock(), which by somehow failed Now we call btrfsbioend_io() to handle the error
3.2 btrfsbioendio() calls the original endio function Which is endbbiodataread(), and it calls bio_put() for the original bio.
Now the original bio is freed.
The submitted first 64K bio finished Now we call into btrfscheckread_bio() and tries to advance the bio iter. But since the original bio (thus its iter) is already freed, we trigger the above use-after free.
And even if the memory is not poisoned/corrupted, we will later call the original endio function, causing a double freeing.
[FIX] Instead of calling btrfsbioendio(), call btrfsorigbbioend_io(), which has the extra check on split bios and do the pr ---truncated---