[PATCH] splice: prevent deadlock when splicing a file to itself

Deepanshu Kartikey posted 1 patch 2 weeks ago
fs/splice.c | 3 +++
1 file changed, 3 insertions(+)
[PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Deepanshu Kartikey 2 weeks ago

When do_splice_direct_actor() is called with the same inode
for both input and output files (either via the same fd or a
dup'd fd), it causes a hung task in blkdev_write_iter().

The deadlock occurs because sendfile() calls do_splice_direct()
which tries to acquire inode_lock_shared() for reading, while
the write side already holds the same inode lock, causing the
task to block indefinitely in rwsem_down_read_slowpath().

Fix this by checking if the input and output files share the
same inode before proceeding, returning -EINVAL if they do.
This mirrors the existing check in do_splice() for the
pipe-to-pipe case where ipipe == opipe.

Reported-by: syzbot+d31a3b77e5cba96b9f69@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=d31a3b77e5cba96b9f69
Tested-by: syzbot+d31a3b77e5cba96b9f69@syzkaller.appspotmail.com
Signed-off-by: Deepanshu Kartikey <Kartikey406@gmail.com>
---
 fs/splice.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/fs/splice.c b/fs/splice.c
index 9d8f63e2fd1a..c0ad1859de34 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -1199,6 +1199,9 @@ static ssize_t do_splice_direct_actor(struct file *in, loff_t *ppos,
 	if (unlikely(out->f_flags & O_APPEND))
 		return -EINVAL;
 
+	/* Prevent deadlock when splicing a file to itself */
+	if (file_inode(in) == file_inode(out))
+		return -EINVAL;
 	ret = splice_direct_to_actor(in, &sd, actor);
 	if (ret > 0)
 		*ppos = sd.pos;
-- 
2.43.0
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Christoph Hellwig 3 days, 15 hours ago
On Fri, Mar 20, 2026 at 06:36:15PM +0530, Deepanshu Kartikey wrote:
> Fix this by checking if the input and output files share the
> same inode before proceeding, returning -EINVAL if they do.
> This mirrors the existing check in do_splice() for the
> pipe-to-pipe case where ipipe == opipe.

While restricting splice to be between difference inodes sounds like a
nice simplification, I'm not sure we can add it 20 years after the
syscall was added.
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Jens Axboe 3 days, 15 hours ago
On 3/31/26 9:10 AM, Christoph Hellwig wrote:
> On Fri, Mar 20, 2026 at 06:36:15PM +0530, Deepanshu Kartikey wrote:
>> Fix this by checking if the input and output files share the
>> same inode before proceeding, returning -EINVAL if they do.
>> This mirrors the existing check in do_splice() for the
>> pipe-to-pipe case where ipipe == opipe.
> 
> While restricting splice to be between difference inodes sounds like a
> nice simplification, I'm not sure we can add it 20 years after the
> syscall was added.

Well if we could break splice all over with:

36e2c7421f02 ("fs: don't allow splice read/write without explicit ops")

then surely this one would be OK too?

-- 
Jens Axboe
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Christoph Hellwig 3 days, 15 hours ago
On Tue, Mar 31, 2026 at 09:15:07AM -0600, Jens Axboe wrote:
> On 3/31/26 9:10 AM, Christoph Hellwig wrote:
> > On Fri, Mar 20, 2026 at 06:36:15PM +0530, Deepanshu Kartikey wrote:
> >> Fix this by checking if the input and output files share the
> >> same inode before proceeding, returning -EINVAL if they do.
> >> This mirrors the existing check in do_splice() for the
> >> pipe-to-pipe case where ipipe == opipe.
> > 
> > While restricting splice to be between difference inodes sounds like a
> > nice simplification, I'm not sure we can add it 20 years after the
> > syscall was added.
> 
> Well if we could break splice all over with:
> 
> 36e2c7421f02 ("fs: don't allow splice read/write without explicit ops")

Well, that had an easy way out by converting instances people actually
used to the iter ops.  Which we ended up doing for a few.

> then surely this one would be OK too?

While this has no way out.  Not that I would complain if it worked,
but splicing into the same file doesn't seem like a too outlandish
idea.  OTOH it probably already didn't work for file systems that
take i_rwsem in the read path like XFS or these days the block
device node.
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Jens Axboe 3 days, 15 hours ago
On 3/31/26 9:21 AM, Christoph Hellwig wrote:
> On Tue, Mar 31, 2026 at 09:15:07AM -0600, Jens Axboe wrote:
>> On 3/31/26 9:10 AM, Christoph Hellwig wrote:
>>> On Fri, Mar 20, 2026 at 06:36:15PM +0530, Deepanshu Kartikey wrote:
>>>> Fix this by checking if the input and output files share the
>>>> same inode before proceeding, returning -EINVAL if they do.
>>>> This mirrors the existing check in do_splice() for the
>>>> pipe-to-pipe case where ipipe == opipe.
>>>
>>> While restricting splice to be between difference inodes sounds like a
>>> nice simplification, I'm not sure we can add it 20 years after the
>>> syscall was added.
>>
>> Well if we could break splice all over with:
>>
>> 36e2c7421f02 ("fs: don't allow splice read/write without explicit ops")
> 
> Well, that had an easy way out by converting instances people actually
> used to the iter ops.  Which we ended up doing for a few.

Sure, and it was the right thing to do, but you still end up with a
broken kernel for that user. Until it gets reported, fixed, and bubbles
back to a kernel that they can use.

IOW, no different than this one.

>> then surely this one would be OK too?
> 
> While this has no way out.  Not that I would complain if it worked,
> but splicing into the same file doesn't seem like a too outlandish
> idea.  OTOH it probably already didn't work for file systems that
> take i_rwsem in the read path like XFS or these days the block
> device node.

There is a way out, it's reverting it. I'd be surprised if this didn't
trigger issues already, like the flagged one.

Thing is, I don't really see an alternative fix to this...

-- 
Jens Axboe
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Jan Kara 2 days, 22 hours ago
On Tue 31-03-26 09:24:29, Jens Axboe wrote:
> On 3/31/26 9:21 AM, Christoph Hellwig wrote:
> > On Tue, Mar 31, 2026 at 09:15:07AM -0600, Jens Axboe wrote:
> >> On 3/31/26 9:10 AM, Christoph Hellwig wrote:
> >>> On Fri, Mar 20, 2026 at 06:36:15PM +0530, Deepanshu Kartikey wrote:
> >>>> Fix this by checking if the input and output files share the
> >>>> same inode before proceeding, returning -EINVAL if they do.
> >>>> This mirrors the existing check in do_splice() for the
> >>>> pipe-to-pipe case where ipipe == opipe.
> >>>
> >>> While restricting splice to be between difference inodes sounds like a
> >>> nice simplification, I'm not sure we can add it 20 years after the
> >>> syscall was added.
> >>
> >> Well if we could break splice all over with:
> >>
> >> 36e2c7421f02 ("fs: don't allow splice read/write without explicit ops")
> > 
> > Well, that had an easy way out by converting instances people actually
> > used to the iter ops.  Which we ended up doing for a few.
> 
> Sure, and it was the right thing to do, but you still end up with a
> broken kernel for that user. Until it gets reported, fixed, and bubbles
> back to a kernel that they can use.
> 
> IOW, no different than this one.
> 
> >> then surely this one would be OK too?
> > 
> > While this has no way out.  Not that I would complain if it worked,
> > but splicing into the same file doesn't seem like a too outlandish
> > idea.  OTOH it probably already didn't work for file systems that
> > take i_rwsem in the read path like XFS or these days the block
> > device node.
> 
> There is a way out, it's reverting it. I'd be surprised if this didn't
> trigger issues already, like the flagged one.
> 
> Thing is, I don't really see an alternative fix to this...

Hum, I'm probably missing something but I just don't see how the proposed
deadlock is supposed to happen. Block devices use filemap_splice_read() as
their .splice_read method. That does not acquire inode lock anywhere
AFAICT. Even if it did, it just reads a chunk of file into the pipe
(internal in-kernel splice pipe) and then we use .splice_write
(iter_file_splice_write() in bdev case) to write the content from the pipe
into the file. So I don't see how the locks between the read and write part
of the pipe would get intertwined...

Also if I check the console log from the syzbot reproducer (and hung task
dump) there's no sign we'd be already holding inode lock - and it would be
lockdep loudly complaining that we're trying to reacquire the same lock,
not hung task detector, if we really did acquire that lock twice. So I just
don't think the patch or the changelog makes sense.

								Honza
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Deepanshu Kartikey 2 days, 21 hours ago
On Wed, Apr 01, 2026, Jan Kara wrote:
> Hum, I'm probably missing something but I just
> don't see how the proposed deadlock is supposed
> to happen.

You are correct, I apologize for the incorrect
analysis in the patch description.

After looking more carefully at the code, the
actual deadlock involves sendfile() and a
concurrent fallocate() on the same inode:

1. sendfile() write side calls blkdev_write_iter()
   which acquires inode_lock_shared().

2. Concurrent fallocate() calls blkdev_fallocate()
   which tries inode_lock() (exclusive) and blocks
   waiting for the shared lock to be released.

3. sendfile() read side then calls
   blkdev_read_iter() which tries
   inode_lock_shared() but is blocked because
   rwsem prevents new readers when a writer
   is waiting.

This creates a circular dependency where nobody
can proceed, causing the hung task.

My original patch was fixing the wrong thing.
I will investigate the correct fix.

Thank you for the careful review!
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Jan Kara 2 days, 19 hours ago
On Wed 01-04-26 14:02:27, Deepanshu Kartikey wrote:
> On Wed, Apr 01, 2026, Jan Kara wrote:
> > Hum, I'm probably missing something but I just
> > don't see how the proposed deadlock is supposed
> > to happen.
> 
> You are correct, I apologize for the incorrect
> analysis in the patch description.
> 
> After looking more carefully at the code, the
> actual deadlock involves sendfile() and a
> concurrent fallocate() on the same inode:
> 
> 1. sendfile() write side calls blkdev_write_iter()
>    which acquires inode_lock_shared().
> 
> 2. Concurrent fallocate() calls blkdev_fallocate()
>    which tries inode_lock() (exclusive) and blocks
>    waiting for the shared lock to be released.
> 
> 3. sendfile() read side then calls
>    blkdev_read_iter() which tries
>    inode_lock_shared() but is blocked because
>    rwsem prevents new readers when a writer
>    is waiting.
> 
> This creates a circular dependency where nobody
> can proceed, causing the hung task.

I'm afraid you're using some LLM for your analysis and it's doing a bad
job. Please verify in the code whether what LLM says to you is in fact
correct (and if you are unable to do that then please learn more before
submitting kernel patches). As a hint, sendfile() for block devices never
ends up calling blkdev_read_iter() (plus there are other bogus things in
the analysis).

								Honza
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Deepanshu Kartikey 2 days, 19 hours ago
On Wed, Apr 1, 2026 at 4:17 PM Jan Kara <jack@suse.cz> wrote:
>
> On Wed 01-04-26 14:02:27, Deepanshu Kartikey wrote:
> > On Wed, Apr 01, 2026, Jan Kara wrote:
> > > Hum, I'm probably missing something but I just
> > > don't see how the proposed deadlock is supposed
> > > to happen.
> >
> > You are correct, I apologize for the incorrect
> > analysis in the patch description.
> >
> > After looking more carefully at the code, the
> > actual deadlock involves sendfile() and a
> > concurrent fallocate() on the same inode:
> >
> > 1. sendfile() write side calls blkdev_write_iter()
> >    which acquires inode_lock_shared().
> >
> > 2. Concurrent fallocate() calls blkdev_fallocate()
> >    which tries inode_lock() (exclusive) and blocks
> >    waiting for the shared lock to be released.
> >
> > 3. sendfile() read side then calls
> >    blkdev_read_iter() which tries
> >    inode_lock_shared() but is blocked because
> >    rwsem prevents new readers when a writer
> >    is waiting.
> >
> > This creates a circular dependency where nobody
> > can proceed, causing the hung task.
>
> I'm afraid you're using some LLM for your analysis and it's doing a bad
> job. Please verify in the code whether what LLM says to you is in fact
> correct (and if you are unable to do that then please learn more before
> submitting kernel patches). As a hint, sendfile() for block devices never
> ends up calling blkdev_read_iter() (plus there are other bogus things in
> the analysis).
>
>                                                                 Honza
> --
> Jan Kara <jack@suse.com>
> SUSE Labs, CR

Noted. I will do proper code analysis myself before sending any
further patches or replies.

Thank you for the guidance.
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Christian Brauner 3 days, 20 hours ago
On Fri, Mar 20, 2026 at 06:36:15PM +0530, Deepanshu Kartikey wrote:
> 
> When do_splice_direct_actor() is called with the same inode
> for both input and output files (either via the same fd or a
> dup'd fd), it causes a hung task in blkdev_write_iter().
> 
> The deadlock occurs because sendfile() calls do_splice_direct()
> which tries to acquire inode_lock_shared() for reading, while
> the write side already holds the same inode lock, causing the
> task to block indefinitely in rwsem_down_read_slowpath().
> 
> Fix this by checking if the input and output files share the
> same inode before proceeding, returning -EINVAL if they do.
> This mirrors the existing check in do_splice() for the
> pipe-to-pipe case where ipipe == opipe.
> 
> Reported-by: syzbot+d31a3b77e5cba96b9f69@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=d31a3b77e5cba96b9f69
> Tested-by: syzbot+d31a3b77e5cba96b9f69@syzkaller.appspotmail.com
> Signed-off-by: Deepanshu Kartikey <Kartikey406@gmail.com>
> ---

@Jens?

>  fs/splice.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/fs/splice.c b/fs/splice.c
> index 9d8f63e2fd1a..c0ad1859de34 100644
> --- a/fs/splice.c
> +++ b/fs/splice.c
> @@ -1199,6 +1199,9 @@ static ssize_t do_splice_direct_actor(struct file *in, loff_t *ppos,
>  	if (unlikely(out->f_flags & O_APPEND))
>  		return -EINVAL;
>  
> +	/* Prevent deadlock when splicing a file to itself */
> +	if (file_inode(in) == file_inode(out))
> +		return -EINVAL;
>  	ret = splice_direct_to_actor(in, &sd, actor);
>  	if (ret > 0)
>  		*ppos = sd.pos;
> -- 
> 2.43.0
>
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Jens Axboe 3 days, 16 hours ago
On 3/31/26 3:33 AM, Christian Brauner wrote:
> On Fri, Mar 20, 2026 at 06:36:15PM +0530, Deepanshu Kartikey wrote:
>>
>> When do_splice_direct_actor() is called with the same inode
>> for both input and output files (either via the same fd or a
>> dup'd fd), it causes a hung task in blkdev_write_iter().
>>
>> The deadlock occurs because sendfile() calls do_splice_direct()
>> which tries to acquire inode_lock_shared() for reading, while
>> the write side already holds the same inode lock, causing the
>> task to block indefinitely in rwsem_down_read_slowpath().
>>
>> Fix this by checking if the input and output files share the
>> same inode before proceeding, returning -EINVAL if they do.
>> This mirrors the existing check in do_splice() for the
>> pipe-to-pipe case where ipipe == opipe.
>>
>> Reported-by: syzbot+d31a3b77e5cba96b9f69@syzkaller.appspotmail.com
>> Closes: https://syzkaller.appspot.com/bug?extid=d31a3b77e5cba96b9f69
>> Tested-by: syzbot+d31a3b77e5cba96b9f69@syzkaller.appspotmail.com
>> Signed-off-by: Deepanshu Kartikey <Kartikey406@gmail.com>
>> ---
> 
> @Jens?

Fix looks reasonable to me.

-- 
Jens Axboe
Re: [PATCH] splice: prevent deadlock when splicing a file to itself
Posted by Deepanshu Kartikey 3 days, 21 hours ago
On Fri, Mar 20, 2026 at 6:36 PM Deepanshu Kartikey
<kartikey406@gmail.com> wrote:
>
>
> When do_splice_direct_actor() is called with the same inode
> for both input and output files (either via the same fd or a
> dup'd fd), it causes a hung task in blkdev_write_iter().
>
> The deadlock occurs because sendfile() calls do_splice_direct()
> which tries to acquire inode_lock_shared() for reading, while
> the write side already holds the same inode lock, causing the
> task to block indefinitely in rwsem_down_read_slowpath().
>
> Fix this by checking if the input and output files share the
> same inode before proceeding, returning -EINVAL if they do.
> This mirrors the existing check in do_splice() for the
> pipe-to-pipe case where ipipe == opipe.
>
> Reported-by: syzbot+d31a3b77e5cba96b9f69@syzkaller.appspotmail.com
> Closes: https://syzkaller.appspot.com/bug?extid=d31a3b77e5cba96b9f69
> Tested-by: syzbot+d31a3b77e5cba96b9f69@syzkaller.appspotmail.com
> Signed-off-by: Deepanshu Kartikey <Kartikey406@gmail.com>
> ---
>  fs/splice.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/fs/splice.c b/fs/splice.c
> index 9d8f63e2fd1a..c0ad1859de34 100644
> --- a/fs/splice.c
> +++ b/fs/splice.c
> @@ -1199,6 +1199,9 @@ static ssize_t do_splice_direct_actor(struct file *in, loff_t *ppos,
>         if (unlikely(out->f_flags & O_APPEND))
>                 return -EINVAL;
>
> +       /* Prevent deadlock when splicing a file to itself */
> +       if (file_inode(in) == file_inode(out))
> +               return -EINVAL;
>         ret = splice_direct_to_actor(in, &sd, actor);
>         if (ret > 0)
>                 *ppos = sd.pos;
> --
> 2.43.0
>

Hi Brauner, viro and Jack

Gentle ping on this patch . I have submitted this patch on 20 march
2026. This patch has been tested in sysbot.

Please let me know if anything else required.

Thanks

Deepanshu