block: fix recursive i_rwsem locking in blkdev_write_iter()

Commit 2fec4fd97c ("block: take i_rwsem for the direct I/O write
fallback") takes i_rwsem around blkdev_buffered_write() in the direct
write fallback to close a race with set_blocksize().  Commit
1b1be9ea4e ("block: take the inode lock for buffered write fallback")
fixes the same race by taking i_rwsem inside blkdev_buffered_write()
instead.  With both commits applied, the fallback path takes i_rwsem
recursively:

  blkdev_write_iter()
    inode_lock_shared(bd_inode)
    blkdev_buffered_write()
      inode_lock_shared(inode)

syzbot reports this as:

  WARNING: possible recursive locking detected
  syz.0.40/6005 is trying to acquire lock:
  ffff888024956f68 (&sb->s_type->i_mutex_key#10){++++}-{4:4}, at: blkdev_buffered_write+0xd7/0x160 block/fops.c:738
  but task is already holding lock:
  ffff888024956f68 (&sb->s_type->i_mutex_key#10){++++}-{4:4}, at: blkdev_write_iter+0x6c5/0x810 block/fops.c:815

rw_semaphore read locks are not recursive: once a writer is queued,
new readers block behind it.  If set_blocksize() calls inode_lock()
between the two acquisitions, it waits for the first read lock to be
released, while the second inode_lock_shared() waits behind
set_blocksize(), and the task deadlocks.

Drop the outer locking in the fallback.

Fixes: 1b1be9ea4e ("block: take the inode lock for buffered write fallback")
Reported-by: syzbot+bd975ef54294bec73f0e@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=bd975ef54294bec73f0e
Cc: Tal Zussman <tz2294@columbia.edu>
Cc: Julian Sun <sunjunchao@bytedance.com>
Assisted-by: LLM
Signed-off-by: Zhenxian Ma <mzx199711@gmail.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Link: https://patch.msgid.link/20261022062513.77372-1-mzx199711@gmail.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Zhenxian Ma 2026-10-22 14:25:13 +08:00 committed by Jens Axboe
parent 87409eedc5
commit 21b88cc8ba

View File

@ -808,9 +808,7 @@ static ssize_t blkdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
} else {
ssize_t ret2;
inode_lock_shared(bd_inode);
ret2 = blkdev_buffered_write(iocb, from);
inode_unlock_shared(bd_inode);
ret = direct_write_fallback(iocb, from, ret,
ret2);
need_sync = true;