Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 33ec3e53 authored by Jan Kara's avatar Jan Kara Committed by Jens Axboe
Browse files

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: default avatar <syzbot+10007d66ca02b08f0e60@syzkaller.appspotmail.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent a278682d
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -945,9 +945,20 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
	if (!file)
		goto out;

	error = mutex_lock_killable(&loop_ctl_mutex);
	/*
	 * 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);
	if (error)
		goto out_bdev;

	error = -EBUSY;
	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);
	if (partscan)
		loop_reread_partitions(lo, bdev);
	if (!(mode & FMODE_EXCL))
		blkdev_put(bdev, mode | FMODE_EXCL);
	return 0;

out_unlock:
	mutex_unlock(&loop_ctl_mutex);
out_bdev:
	if (!(mode & FMODE_EXCL))
		blkdev_put(bdev, mode | FMODE_EXCL);
out_putf:
	fput(file);
out: