forked from mirrors/linux
loop: Don't change loop device under exclusive opener
Loop module allows calling LOOP_SET_FD while there are other openers of
the loop device. Even exclusive ones. This can lead to weird
consequences such as kernel deadlocks like:
mount_bdev() lo_ioctl()
udf_fill_super()
udf_load_vrs()
sb_set_blocksize() - sets desired block size B
udf_tread()
sb_bread()
__bread_gfp(bdev, block, B)
loop_set_fd()
set_blocksize()
- now __getblk_slow() indefinitely loops because B != bdev
block size
Fix the problem by disallowing LOOP_SET_FD ioctl when there are
exclusive openers of a loop device.
[Deliberately chosen not to CC stable as a user with priviledges to
trigger this race has other means of taking the system down and this
has a potential of breaking some weird userspace setup]
Reported-and-tested-by: syzbot+10007d66ca02b08f0e60@syzkaller.appspotmail.com
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
a278682dad
commit
33ec3e53e7
1 changed files with 17 additions and 1 deletions
|
|
@ -945,9 +945,20 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
|
||||||
if (!file)
|
if (!file)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we don't hold exclusive handle for the device, upgrade to it
|
||||||
|
* here to avoid changing device under exclusive owner.
|
||||||
|
*/
|
||||||
|
if (!(mode & FMODE_EXCL)) {
|
||||||
|
bdgrab(bdev);
|
||||||
|
error = blkdev_get(bdev, mode | FMODE_EXCL, loop_set_fd);
|
||||||
|
if (error)
|
||||||
|
goto out_putf;
|
||||||
|
}
|
||||||
|
|
||||||
error = mutex_lock_killable(&loop_ctl_mutex);
|
error = mutex_lock_killable(&loop_ctl_mutex);
|
||||||
if (error)
|
if (error)
|
||||||
goto out_putf;
|
goto out_bdev;
|
||||||
|
|
||||||
error = -EBUSY;
|
error = -EBUSY;
|
||||||
if (lo->lo_state != Lo_unbound)
|
if (lo->lo_state != Lo_unbound)
|
||||||
|
|
@ -1012,10 +1023,15 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
|
||||||
mutex_unlock(&loop_ctl_mutex);
|
mutex_unlock(&loop_ctl_mutex);
|
||||||
if (partscan)
|
if (partscan)
|
||||||
loop_reread_partitions(lo, bdev);
|
loop_reread_partitions(lo, bdev);
|
||||||
|
if (!(mode & FMODE_EXCL))
|
||||||
|
blkdev_put(bdev, mode | FMODE_EXCL);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
mutex_unlock(&loop_ctl_mutex);
|
mutex_unlock(&loop_ctl_mutex);
|
||||||
|
out_bdev:
|
||||||
|
if (!(mode & FMODE_EXCL))
|
||||||
|
blkdev_put(bdev, mode | FMODE_EXCL);
|
||||||
out_putf:
|
out_putf:
|
||||||
fput(file);
|
fput(file);
|
||||||
out:
|
out:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue