From 21b88cc8bafceee6c6c82503e3b2cc6f1d0c78bf Mon Sep 17 00:00:00 2001 From: Zhenxian Ma Date: Thu, 22 Oct 2026 14:25:13 +0800 Subject: [PATCH] block: fix recursive i_rwsem locking in blkdev_write_iter() Commit 2fec4fd97cc3 ("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 1b1be9ea4e34 ("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: 1b1be9ea4e34 ("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 Cc: Julian Sun Assisted-by: LLM Signed-off-by: Zhenxian Ma Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20261022062513.77372-1-mzx199711@gmail.com Signed-off-by: Jens Axboe --- block/fops.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/block/fops.c b/block/fops.c index 517b96c9d262..375ec2c15926 100644 --- a/block/fops.c +++ b/block/fops.c @@ -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;