Forwarded: [PATCH] loop: block changing lo_offset/lo_sizelimit on mounted device

syzbot posted 1 patch 1 day, 22 hours ago
block/bdev.c              |  5 -----
drivers/block/loop.c      | 16 ++++++++++++++++
include/linux/blk_types.h |  5 +++++
3 files changed, 21 insertions(+), 5 deletions(-)
Forwarded: [PATCH] loop: block changing lo_offset/lo_sizelimit on mounted device
Posted by syzbot 1 day, 22 hours ago
For archival purposes, forwarding an incoming command email to
linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com.

***

Subject: [PATCH] loop: block changing lo_offset/lo_sizelimit on mounted device
Author: kartikey406@gmail.com

#syz test: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git master

LOOP_SET_STATUS{64} allows changing lo_offset and shrinking
lo_sizelimit while a filesystem is mounted on the loop device.
This effectively mutates the data visible to the mounted filesystem,
which is equivalent to writing directly to the block device.

When CONFIG_BLK_DEV_WRITE_MOUNTED is disabled, direct writes to a
mounted block device are blocked. However, LOOP_SET_STATUS{64}
bypasses this protection because it modifies the loop configuration
through an ioctl rather than opening the block device for writing.

Fix this by checking bdev_writes_blocked() before allowing changes
to lo_offset or shrinking lo_sizelimit. If the loop device has
writes blocked, return -EBUSY. Increasing lo_sizelimit is still
allowed since growing the device is harmless and has legitimate
use cases such as online resize.

Move bdev_writes_blocked() from block/bdev.c to
include/linux/blk_types.h as a static inline function so it can
be used from the loop driver without exporting a symbol.

Reported-by: syzbot+fb32afec111a7d61b939@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=fb32afec111a7d61b939
Signed-off-by: Deepanshu Kartikey <kartikey406@gmail.com>
---
v2:
  - Use #ifndef CONFIG_BLK_DEV_WRITE_MOUNTED instead of exporting
    bdev_writes_blocked(), as suggested by Ted Ts'o
  - Move bdev_writes_blocked() to include/linux/blk_types.h as
    static inline instead of exporting from block/bdev.c, as
    suggested by Christoph Hellwig
  - Allow increasing lo_sizelimit since growing the device is
    harmless, as pointed out by Christoph Hellwig
  - Remove spurious empty line
---
 block/bdev.c              |  5 -----
 drivers/block/loop.c      | 16 ++++++++++++++++
 include/linux/blk_types.h |  5 +++++
 3 files changed, 21 insertions(+), 5 deletions(-)

diff --git a/block/bdev.c b/block/bdev.c
index ed022f8c48c7..e0bace1a6c27 100644
--- a/block/bdev.c
+++ b/block/bdev.c
@@ -860,11 +860,6 @@ void blkdev_put_no_open(struct block_device *bdev)
 	put_device(&bdev->bd_device);
 }
 
-static bool bdev_writes_blocked(struct block_device *bdev)
-{
-	return bdev->bd_writers < 0;
-}
-
 static void bdev_block_writes(struct block_device *bdev)
 {
 	bdev->bd_writers--;
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 0000913f7efc..34bbbf3bcb36 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1239,6 +1239,22 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
 		goto out_unlock;
 	}
 
+#ifndef CONFIG_BLK_DEV_WRITE_MOUNTED
+	/*
+	 * Changing lo_offset or shrinking lo_sizelimit on a mounted
+	 * device is equivalent to modifying the block device contents.
+	 * Block this if writes to the device are blocked.
+	 */
+	if ((lo->lo_offset != info->lo_offset ||
+	     (info->lo_sizelimit &&
+	      (lo->lo_sizelimit == 0 ||
+	       info->lo_sizelimit < lo->lo_sizelimit))) &&
+	    bdev_writes_blocked(lo->lo_device)) {
+		err = -EBUSY;
+		goto out_unlock;
+	}
+#endif
+
 	if (lo->lo_offset != info->lo_offset ||
 	    lo->lo_sizelimit != info->lo_sizelimit) {
 		size_changed = true;
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 8808ee76e73c..82ece8737b85 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -84,6 +84,11 @@ struct block_device {
 #define bdev_whole(_bdev) \
 	((_bdev)->bd_disk->part0)
 
+static inline bool bdev_writes_blocked(struct block_device *bdev)
+{
+	return bdev->bd_writers < 0;
+}
+
 #define dev_to_bdev(device) \
 	container_of((device), struct block_device, bd_device)
 
-- 
2.43.0