From: Yongpeng Yang <yangyongpeng.storage@gmail.com>
To: Raphael Pinsonneault-Thibeault <rpthibeault@gmail.com>, axboe@kernel.dk
Cc: linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
jack@suse.cz, cascardo@igalia.com,
linux-kernel-mentees@lists.linux.dev, skhan@linuxfoundation.org,
syzbot+3ee481e21fd75e14c397@syzkaller.appspotmail.com,
Yongpeng Yang <yangyongpeng@xiaomi.com>
Subject: Re: [PATCH] loop: don't change loop device under exclusive opener in loop_set_status
Date: Tue, 18 Nov 2025 15:10:20 +0800 [thread overview]
Message-ID: <93a1773e-e30a-469d-bc8f-029773112401@gmail.com> (raw)
In-Reply-To: <20251114144204.2402336-2-rpthibeault@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 6902 bytes --]
On 11/14/25 22:42, Raphael Pinsonneault-Thibeault wrote:
> loop_set_status() is allowed to change the loop device while there
> are other openers of the device, even exclusive ones.
>
> In this case, it causes a KASAN: slab-out-of-bounds Read in
> ext4_search_dir(), since when looking for an entry in an inlined
> directory, e_value_offs is changed underneath the filesystem by
> loop_set_status().
>
> Fix the problem by forbidding loop_set_status() from modifying the loop
> device while there are exclusive openers of the device. This is similar
> to the fix in loop_configure() by commit 33ec3e53e7b1 ("loop: Don't
> change loop device under exclusive opener") alongside commit ecbe6bc0003b
> ("block: use bd_prepare_to_claim directly in the loop driver").
>
> Reported-by: syzbot+3ee481e21fd75e14c397@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=3ee481e21fd75e14c397
> Tested-by: syzbot+3ee481e21fd75e14c397@syzkaller.appspotmail.com
> Signed-off-by: Raphael Pinsonneault-Thibeault <rpthibeault@gmail.com>
> ---
> ML thread for previous, misguided patch idea:
> https://lore.kernel.org/all/20251112185712.2031993-2-rpthibeault@gmail.com/t/
>
> drivers/block/loop.c | 41 ++++++++++++++++++++++++++++++-----------
> 1 file changed, 30 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/block/loop.c b/drivers/block/loop.c
> index 053a086d547e..756ee682e767 100644
> --- a/drivers/block/loop.c
> +++ b/drivers/block/loop.c
> @@ -1222,13 +1222,24 @@ static int loop_clr_fd(struct loop_device *lo)
> }
>
> static int
> -loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
> +loop_set_status(struct loop_device *lo, blk_mode_t mode,
> + struct block_device *bdev, const struct loop_info64 *info)
> {
> int err;
> bool partscan = false;
> bool size_changed = false;
> unsigned int memflags;
>
> + /*
> + * If we don't hold exclusive handle for the device, upgrade to it
> + * here to avoid changing device under exclusive owner.
> + */
> + if (!(mode & BLK_OPEN_EXCL)) {
> + err = bd_prepare_to_claim(bdev, loop_set_status, NULL);
> + if (err)
> + goto out_reread_partitions;
> + }
> +
+ if (mode & BLK_OPEN_EXCL) {
+ struct block_device *whole = bdev_whole(bdev);
+
+ BUG_ON(whole->bd_claiming == NULL);
+ }
I add the above code and do the following test:
# losetup -f data.1g
# echo "0 `blockdev --getsz /dev/loop0` linear /dev/loop0 0" | dmsetup
create my-linear
# ./ioctl-test /dev/mapper/my-linear // trigger BUG_ON, ioctl-test.c is
in attachment.
The root causes of BUG_ON:
1. When creating 'my-linear' device, the mode for opening /dev/loop0
does not include the BLK_OPEN_EXCL flag.
table_load
- dm_table_create // get_mode() never assign BLK_OPEN_EXCL to {struct
dm_table *t}->mode
- populate_table
- dm_table_add_target
- linear_ctr
- dm_get_device // mode = {struct dm_table *t}->mode, never open
loop0 with BLK_OPEN_EXCL mode.
2. When 'my-linear' device is opened with the O_EXCL flag, and an ioctl
is issued to it. The dm_blk_ioctl function calls bdev->bd_disk->fops-
> ioctl(bdev, mode, cmd, arg), which passes the mode with BLK_OPEN_EXCL
flag to lo_ioctl.
3. loop0 was not opened by dm_get_device() in BLK_OPEN_EXCL mode. As a
result, whole->bd_claiming is NULL.
Thus, the BLK_OPEN_EXCL flag in the mode passed to lo_ioctl doesn't
guarantee the loop device was opened with BLK_OPEN_EXCL.
How about use per-device rw_semaphore instead of 'bd_prepare_to_claim/
bd_abort_claiming'?
Yongpeng,
> err = mutex_lock_killable(&lo->lo_mutex);
> if (err)
> return err;
> @@ -1270,6 +1281,9 @@ loop_set_status(struct loop_device *lo, const struct loop_info64 *info)
> }
> out_unlock:
> mutex_unlock(&lo->lo_mutex);
> + if (!(mode & BLK_OPEN_EXCL))
> + bd_abort_claiming(bdev, loop_set_status);
> +out_reread_partitions:
> if (partscan)
> loop_reread_partitions(lo);
>
> @@ -1349,7 +1363,9 @@ loop_info64_to_old(const struct loop_info64 *info64, struct loop_info *info)
> }
>
> static int
> -loop_set_status_old(struct loop_device *lo, const struct loop_info __user *arg)
> +loop_set_status_old(struct loop_device *lo, blk_mode_t mode,
> + struct block_device *bdev,
> + const struct loop_info __user *arg)
> {
> struct loop_info info;
> struct loop_info64 info64;
> @@ -1357,17 +1373,19 @@ loop_set_status_old(struct loop_device *lo, const struct loop_info __user *arg)
> if (copy_from_user(&info, arg, sizeof (struct loop_info)))
> return -EFAULT;
> loop_info64_from_old(&info, &info64);
> - return loop_set_status(lo, &info64);
> + return loop_set_status(lo, mode, bdev, &info64);
> }
>
> static int
> -loop_set_status64(struct loop_device *lo, const struct loop_info64 __user *arg)
> +loop_set_status64(struct loop_device *lo, blk_mode_t mode,
> + struct block_device *bdev,
> + const struct loop_info64 __user *arg)
> {
> struct loop_info64 info64;
>
> if (copy_from_user(&info64, arg, sizeof (struct loop_info64)))
> return -EFAULT;
> - return loop_set_status(lo, &info64);
> + return loop_set_status(lo, mode, bdev, &info64);
> }
>
> static int
> @@ -1546,14 +1564,14 @@ static int lo_ioctl(struct block_device *bdev, blk_mode_t mode,
> case LOOP_SET_STATUS:
> err = -EPERM;
> if ((mode & BLK_OPEN_WRITE) || capable(CAP_SYS_ADMIN))
> - err = loop_set_status_old(lo, argp);
> + err = loop_set_status_old(lo, mode, bdev, argp);
> break;
> case LOOP_GET_STATUS:
> return loop_get_status_old(lo, argp);
> case LOOP_SET_STATUS64:
> err = -EPERM;
> if ((mode & BLK_OPEN_WRITE) || capable(CAP_SYS_ADMIN))
> - err = loop_set_status64(lo, argp);
> + err = loop_set_status64(lo, mode, bdev, argp);
> break;
> case LOOP_GET_STATUS64:
> return loop_get_status64(lo, argp);
> @@ -1647,8 +1665,9 @@ loop_info64_to_compat(const struct loop_info64 *info64,
> }
>
> static int
> -loop_set_status_compat(struct loop_device *lo,
> - const struct compat_loop_info __user *arg)
> +loop_set_status_compat(struct loop_device *lo, blk_mode_t mode,
> + struct block_device *bdev,
> + const struct compat_loop_info __user *arg)
> {
> struct loop_info64 info64;
> int ret;
> @@ -1656,7 +1675,7 @@ loop_set_status_compat(struct loop_device *lo,
> ret = loop_info64_from_compat(arg, &info64);
> if (ret < 0)
> return ret;
> - return loop_set_status(lo, &info64);
> + return loop_set_status(lo, mode, bdev, &info64);
> }
>
> static int
> @@ -1682,7 +1701,7 @@ static int lo_compat_ioctl(struct block_device *bdev, blk_mode_t mode,
>
> switch(cmd) {
> case LOOP_SET_STATUS:
> - err = loop_set_status_compat(lo,
> + err = loop_set_status_compat(lo, mode, bdev,
> (const struct compat_loop_info __user *)arg);
> break;
> case LOOP_GET_STATUS:
[-- Attachment #2: ioctl-test.c --]
[-- Type: text/x-csrc, Size: 770 bytes --]
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <errno.h>
#include <string.h>
#include <linux/loop.h>
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s [dev path]\n", argv[0]);
return 1;
}
const char *dev = argv[1];
struct loop_info64 info;
memset(&info, 0, sizeof(info));
int fd = open(dev, O_RDWR | O_EXCL);
if (fd < 0) {
fprintf(stderr, "Failed to open %s exclusively: %s\n",
dev, strerror(errno));
return 1;
}
if (ioctl(fd, LOOP_SET_STATUS64, &info) == -1) {
perror("ioctl error");
close(fd);
return 1;
}
close(fd);
return 0;
}
next prev parent reply other threads:[~2025-11-18 7:10 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-14 14:42 [PATCH] loop: don't change loop device under exclusive opener in loop_set_status Raphael Pinsonneault-Thibeault
2025-11-18 7:10 ` Yongpeng Yang [this message]
2025-12-01 12:38 ` Jan Kara
2025-12-02 9:03 ` Yongpeng Yang
2025-12-02 10:07 ` Jan Kara
2025-12-17 17:48 ` Jan Kara
2025-12-17 19:00 ` [PATCH v2] " Raphael Pinsonneault-Thibeault
2026-01-06 12:08 ` Jan Kara
2026-01-06 12:31 ` Jens Axboe
2026-01-06 12:30 ` Jens Axboe
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=93a1773e-e30a-469d-bc8f-029773112401@gmail.com \
--to=yangyongpeng.storage@gmail.com \
--cc=axboe@kernel.dk \
--cc=cascardo@igalia.com \
--cc=jack@suse.cz \
--cc=linux-block@vger.kernel.org \
--cc=linux-kernel-mentees@lists.linux.dev \
--cc=linux-kernel@vger.kernel.org \
--cc=rpthibeault@gmail.com \
--cc=skhan@linuxfoundation.org \
--cc=syzbot+3ee481e21fd75e14c397@syzkaller.appspotmail.com \
--cc=yangyongpeng@xiaomi.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.