diff options
| author | Ming-Hung Tsai <mtsai@redhat.com> | 2026-02-09 15:54:05 +0800 |
|---|---|---|
| committer | Mikulas Patocka <mpatocka@redhat.com> | 2026-03-02 16:49:04 +0100 |
| commit | 7d1f98d668ee34c1d15bdc0420fdd062f24a27c0 (patch) | |
| tree | 2f60a1883651871f14963967cc33398cf4d9d08b | |
| parent | b3929b2cc2a6003b8e301e6540c651e60d24dcb4 (diff) | |
| download | linux-7d1f98d668ee34c1d15bdc0420fdd062f24a27c0.tar.gz linux-7d1f98d668ee34c1d15bdc0420fdd062f24a27c0.zip | |
dm cache: fix null-deref with concurrent writes in passthrough mode
In passthrough mode, when dm-cache starts to invalidate a cache
entry and bio prison cell lock fails due to concurrent write to
the same cached block, mg->cell remains NULL. The error path in
invalidate_complete() attempts to unlock and free the cell
unconditionally, causing a NULL pointer dereference:
KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007]
CPU: 0 UID: 0 PID: 134 Comm: fio Not tainted 6.19.0-rc7 #3 PREEMPT
RIP: 0010:dm_cell_unlock_v2+0x3f/0x210
<snip>
Call Trace:
invalidate_complete+0xef/0x430
map_bio+0x130f/0x1a10
cache_map+0x320/0x6b0
__map_bio+0x458/0x510
dm_submit_bio+0x40e/0x16d0
__submit_bio+0x419/0x870
<snip>
Reproduce steps:
1. Create a cache device
dmsetup create cmeta --table "0 8192 linear /dev/sdc 0"
dmsetup create cdata --table "0 131072 linear /dev/sdc 8192"
dmsetup create corig --table "0 262144 linear /dev/sdc 262144"
dd if=/dev/zero of=/dev/mapper/cmeta bs=4k count=1 oflag=direct
dmsetup create cache --table "0 262144 cache /dev/mapper/cmeta \
/dev/mapper/cdata /dev/mapper/corig 128 2 metadata2 writethrough smq 0"
2. Promote the first data block into cache
fio --filename=/dev/mapper/cache --name=populate --rw=write --bs=4k \
--direct=1 --size=64k
3. Reload the cache into passthrough mode
dmsetup suspend cache
dmsetup reload cache --table "0 262144 cache /dev/mapper/cmeta \
/dev/mapper/cdata /dev/mapper/corig 128 2 metadata2 passthrough smq 0"
dmsetup resume cache
4. Write to the first cached block concurrently
fio --filename=/dev/mapper/cache --name test --rw=randwrite --bs=4k \
--randrepeat=0 --direct=1 --numjobs=2 --size 64k
Fix by checking if mg->cell is valid before attempting to unlock it.
Fixes: b29d4986d0da ("dm cache: significant rework to leverage dm-bio-prison-v2")
Signed-off-by: Ming-Hung Tsai <mtsai@redhat.com>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
| -rw-r--r-- | drivers/md/dm-cache-target.c | 6 |
1 files changed, 4 insertions, 2 deletions
diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 935ab79b1d0c..ae1edffd14ea 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -1462,8 +1462,10 @@ static void invalidate_complete(struct dm_cache_migration *mg, bool success) struct cache *cache = mg->cache; bio_list_init(&bios); - if (dm_cell_unlock_v2(cache->prison, mg->cell, &bios)) - free_prison_cell(cache, mg->cell); + if (mg->cell) { + if (dm_cell_unlock_v2(cache->prison, mg->cell, &bios)) + free_prison_cell(cache, mg->cell); + } if (!success && mg->overwrite_bio) bio_io_error(mg->overwrite_bio); |
