Skip to content

Commit bdae097

Browse files
jankarakawasaki
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: [email protected] Signed-off-by: Jan Kara <[email protected]>
1 parent 87bbbbc commit bdae097

1 file changed

Lines changed: 30 additions & 8 deletions

File tree

drivers/block/loop.c

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

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

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

14431460
if (lo->lo_queue->limits.logical_block_size == arg)
1444-
return 0;
1461+
goto unlock;
14451462

14461463
sync_blockdev(lo->lo_device);
14471464
invalidate_bdev(lo->lo_device);
@@ -1454,6 +1471,11 @@ static int loop_set_block_size(struct loop_device *lo, unsigned long arg)
14541471
loop_update_dio(lo);
14551472
blk_mq_unfreeze_queue(lo->lo_queue, memflags);
14561473

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

@@ -1472,9 +1494,6 @@ static int lo_simple_ioctl(struct loop_device *lo, unsigned int cmd,
14721494
case LOOP_SET_DIRECT_IO:
14731495
err = loop_set_dio(lo, arg);
14741496
break;
1475-
case LOOP_SET_BLOCK_SIZE:
1476-
err = loop_set_block_size(lo, arg);
1477-
break;
14781497
default:
14791498
err = -EINVAL;
14801499
}
@@ -1529,9 +1548,12 @@ static int lo_ioctl(struct block_device *bdev, blk_mode_t mode,
15291548
break;
15301549
case LOOP_GET_STATUS64:
15311550
return loop_get_status64(lo, argp);
1551+
case LOOP_SET_BLOCK_SIZE:
1552+
if (!(mode & BLK_OPEN_WRITE) && !capable(CAP_SYS_ADMIN))
1553+
return -EPERM;
1554+
return loop_set_block_size(lo, mode, bdev, arg);
15321555
case LOOP_SET_CAPACITY:
15331556
case LOOP_SET_DIRECT_IO:
1534-
case LOOP_SET_BLOCK_SIZE:
15351557
if (!(mode & BLK_OPEN_WRITE) && !capable(CAP_SYS_ADMIN))
15361558
return -EPERM;
15371559
fallthrough;

0 commit comments

Comments
 (0)