Skip to content

Commit 7e49538

Browse files
jankaraaxboe
authored andcommitted
loop: Avoid updating block size under exclusive owner
Syzbot came up with a reproducer where a loop device block size is changed underneath a mounted filesystem. This causes a mismatch between the block device block size and the block size stored in the superblock causing confusion in various places such as fs/buffer.c. The particular issue triggered by syzbot was a warning in __getblk_slow() due to requested buffer size not matching block device block size. Fix the problem by getting exclusive hold of the loop device to change its block size. This fails if somebody (such as filesystem) has already an exclusive ownership of the block device and thus prevents modifying the loop device under some exclusive owner which doesn't expect it. Reported-by: syzbot+01ef7a8da81a975e1ccd@syzkaller.appspotmail.com Signed-off-by: Jan Kara <jack@suse.cz> Tested-by: syzbot+01ef7a8da81a975e1ccd@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/20250711163202.19623-2-jack@suse.cz Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 1bb94ff commit 7e49538

File tree

1 file changed

+30
-8
lines changed

1 file changed

+30
-8
lines changed

drivers/block/loop.c

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1432,17 +1432,34 @@ static int loop_set_dio(struct loop_device *lo, unsigned long arg)
14321432
return 0;
14331433
}
14341434

1435-
static int loop_set_block_size(struct loop_device *lo, unsigned long arg)
1435+
static int loop_set_block_size(struct loop_device *lo, blk_mode_t mode,
1436+
struct block_device *bdev, unsigned long arg)
14361437
{
14371438
struct queue_limits lim;
14381439
unsigned int memflags;
14391440
int err = 0;
14401441

1441-
if (lo->lo_state != Lo_bound)
1442-
return -ENXIO;
1442+
/*
1443+
* If we don't hold exclusive handle for the device, upgrade to it
1444+
* here to avoid changing device under exclusive owner.
1445+
*/
1446+
if (!(mode & BLK_OPEN_EXCL)) {
1447+
err = bd_prepare_to_claim(bdev, loop_set_block_size, NULL);
1448+
if (err)
1449+
return err;
1450+
}
1451+
1452+
err = mutex_lock_killable(&lo->lo_mutex);
1453+
if (err)
1454+
goto abort_claim;
1455+
1456+
if (lo->lo_state != Lo_bound) {
1457+
err = -ENXIO;
1458+
goto unlock;
1459+
}
14431460

14441461
if (lo->lo_queue->limits.logical_block_size == arg)
1445-
return 0;
1462+
goto unlock;
14461463

14471464
sync_blockdev(lo->lo_device);
14481465
invalidate_bdev(lo->lo_device);
@@ -1455,6 +1472,11 @@ static int loop_set_block_size(struct loop_device *lo, unsigned long arg)
14551472
loop_update_dio(lo);
14561473
blk_mq_unfreeze_queue(lo->lo_queue, memflags);
14571474

1475+
unlock:
1476+
mutex_unlock(&lo->lo_mutex);
1477+
abort_claim:
1478+
if (!(mode & BLK_OPEN_EXCL))
1479+
bd_abort_claiming(bdev, loop_set_block_size);
14581480
return err;
14591481
}
14601482

@@ -1473,9 +1495,6 @@ static int lo_simple_ioctl(struct loop_device *lo, unsigned int cmd,
14731495
case LOOP_SET_DIRECT_IO:
14741496
err = loop_set_dio(lo, arg);
14751497
break;
1476-
case LOOP_SET_BLOCK_SIZE:
1477-
err = loop_set_block_size(lo, arg);
1478-
break;
14791498
default:
14801499
err = -EINVAL;
14811500
}
@@ -1530,9 +1549,12 @@ static int lo_ioctl(struct block_device *bdev, blk_mode_t mode,
15301549
break;
15311550
case LOOP_GET_STATUS64:
15321551
return loop_get_status64(lo, argp);
1552+
case LOOP_SET_BLOCK_SIZE:
1553+
if (!(mode & BLK_OPEN_WRITE) && !capable(CAP_SYS_ADMIN))
1554+
return -EPERM;
1555+
return loop_set_block_size(lo, mode, bdev, arg);
15331556
case LOOP_SET_CAPACITY:
15341557
case LOOP_SET_DIRECT_IO:
1535-
case LOOP_SET_BLOCK_SIZE:
15361558
if (!(mode & BLK_OPEN_WRITE) && !capable(CAP_SYS_ADMIN))
15371559
return -EPERM;
15381560
fallthrough;

0 commit comments

Comments
 (0)