All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yongpeng Yang <yangyongpeng.storage@gmail.com>
To: Jan Kara <jack@suse.cz>, Yongpeng Yang <yangyongpeng.storage@gmail.com>
Cc: Raphael Pinsonneault-Thibeault <rpthibeault@gmail.com>,
	axboe@kernel.dk, linux-block@vger.kernel.org,
	linux-kernel@vger.kernel.org, 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, 2 Dec 2025 17:03:14 +0800	[thread overview]
Message-ID: <7fac334b-17d3-4c48-8303-2d7e73ff281b@gmail.com> (raw)
In-Reply-To: <zoc2xguhdvjkon36d67usvf2wpufcreiti22sjgekhbhtkvdkp@iezwglvxdw4t>

On 12/1/25 20:38, Jan Kara wrote:
> On Tue 18-11-25 15:10:20, Yongpeng Yang wrote:
>> 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.
> 
> BLK_OPEN_EXCL is added by bdev_open() whenever it is called with non-NULL
> holder. And DM code (open_table_device()) calls bdev_file_open_by_dev() with
> _dm_claim_ptr as the holder. So all opens from DM should be exclusive ones.
> The question obviously is what is broken in this that your reproducer still
> works...
> 
> 								Honza
> 

Yes, I was mistaken. When loop0's whole->bd_claiming is NULL,
bdev->bd_holder is set to _dm_claim_ptr by bd_finish_claiming. When the
my-linear device is opened with BLK_OPEN_EXCL, its ioctls are handled
normally. If the my-linear device is opened without BLK_OPEN_EXCL, the
ioctl call returns -EBUSY, which is the expected behavior.

bdev_open
     - bd_prepare_to_claim //whole->bd_claiming = _dm_claim_ptr;
     - bd_finish_claiming // whole->bd_holder = bd_may_claim; 
bdev->bd_holder = _dm_claim_ptr;
         - bd_clear_claiming // whole->bd_claiming = NULL;

Tested-by: Yongpeng Yang <yangyongpeng@xiaomi.com>

Yongpeng,

>> 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:
> 
> 


  reply	other threads:[~2025-12-02  9:03 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
2025-12-01 12:38   ` Jan Kara
2025-12-02  9:03     ` Yongpeng Yang [this message]
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=7fac334b-17d3-4c48-8303-2d7e73ff281b@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.