diff mbox series

[net-next] splice, net: Fix splice_to_socket() to handle pipe bufs larger than a page

Message ID 1428985.1686737388@warthog.procyon.org.uk (mailing list archive)
State Accepted
Commit ca2d49f77ce4531c74ba207b1e07b55f5ced5ab4
Delegated to: Netdev Maintainers
Headers show
Series [net-next] splice, net: Fix splice_to_socket() to handle pipe bufs larger than a page | expand

Checks

Context Check Description
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 10 this patch: 10
netdev/cc_maintainers success CCed 5 of 5 maintainers
netdev/build_clang success Errors and warnings before: 8 this patch: 8
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success Fixes tag looks correct
netdev/build_allmodconfig_warn success Errors and warnings before: 10 this patch: 10
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 19 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

David Howells June 14, 2023, 10:09 a.m. UTC
splice_to_socket() assumes that a pipe_buffer won't hold more than a single
page of data - but this assumption can be violated by skb_splice_bits()
when it splices from a socket into a pipe.

The problem is that splice_to_socket() doesn't advance the pipe_buffer
length and offset when transcribing from the pipe buf into a bio_vec, so if
the buf is >PAGE_SIZE, it keeps repeating the same initial chunk and
doesn't advance the tail index.  It then subtracts this from "remain" and
overcounts the amount of data to be sent.

The cleanup phase then tries to overclean the pipe, hits an unused pipe buf
and a NULL-pointer dereference occurs.

Fix this by not restricting the bio_vec size to PAGE_SIZE and instead
transcribing the entirety of each pipe_buffer into a single bio_vec and
advancing the tail index if remain hasn't hit zero yet.

Large bio_vecs will then be split up by iterator functions such as
iov_iter_extract_pages().

This resulted in a KASAN report looking like:

general protection fault, probably for non-canonical address 0xdffffc0000000001: 0000 [#1] PREEMPT SMP KASAN
KASAN: null-ptr-deref in range [0x0000000000000008-0x000000000000000f]
...
RIP: 0010:pipe_buf_release include/linux/pipe_fs_i.h:203 [inline]
RIP: 0010:splice_to_socket+0xa91/0xe30 fs/splice.c:933

Fixes: 2dc334f1a63a ("splice, net: Use sendmsg(MSG_SPLICE_PAGES) rather than ->sendpage()")
Reported-by: syzbot+f9e28a23426ac3b24f20@syzkaller.appspotmail.com
Link: https://lore.kernel.org/r/0000000000000900e905fdeb8e39@google.com/
Tested-by: syzbot+f9e28a23426ac3b24f20@syzkaller.appspotmail.com
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Willem de Bruijn <willemdebruijn.kernel@gmail.com>
cc: David Ahern <dsahern@kernel.org>
cc: "David S. Miller" <davem@davemloft.net>
cc: Eric Dumazet <edumazet@google.com>
cc: Jakub Kicinski <kuba@kernel.org>
cc: Paolo Abeni <pabeni@redhat.com>
cc: Jens Axboe <axboe@kernel.dk>
cc: Matthew Wilcox <willy@infradead.org>
cc: Christian Brauner <brauner@kernel.org>
cc: Alexander Viro <viro@zeniv.linux.org.uk>
cc: netdev@vger.kernel.org
cc: linux-fsdevel@vger.kernel.org
---
 fs/splice.c |    6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

Comments

patchwork-bot+netdevbpf@kernel.org June 16, 2023, 6 a.m. UTC | #1
Hello:

This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Wed, 14 Jun 2023 11:09:48 +0100 you wrote:
> splice_to_socket() assumes that a pipe_buffer won't hold more than a single
> page of data - but this assumption can be violated by skb_splice_bits()
> when it splices from a socket into a pipe.
> 
> The problem is that splice_to_socket() doesn't advance the pipe_buffer
> length and offset when transcribing from the pipe buf into a bio_vec, so if
> the buf is >PAGE_SIZE, it keeps repeating the same initial chunk and
> doesn't advance the tail index.  It then subtracts this from "remain" and
> overcounts the amount of data to be sent.
> 
> [...]

Here is the summary with links:
  - [net-next] splice, net: Fix splice_to_socket() to handle pipe bufs larger than a page
    https://git.kernel.org/netdev/net-next/c/ca2d49f77ce4

You are awesome, thank you!
diff mbox series

Patch

diff --git a/fs/splice.c b/fs/splice.c
index e337630aed64..567a1f03ea1e 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -886,7 +886,6 @@  ssize_t splice_to_socket(struct pipe_inode_info *pipe, struct file *out,
 			}
 
 			seg = min_t(size_t, remain, buf->len);
-			seg = min_t(size_t, seg, PAGE_SIZE);
 
 			ret = pipe_buf_confirm(pipe, buf);
 			if (unlikely(ret)) {
@@ -897,10 +896,9 @@  ssize_t splice_to_socket(struct pipe_inode_info *pipe, struct file *out,
 
 			bvec_set_page(&bvec[bc++], buf->page, seg, buf->offset);
 			remain -= seg;
-			if (seg >= buf->len)
-				tail++;
-			if (bc >= ARRAY_SIZE(bvec))
+			if (remain == 0 || bc >= ARRAY_SIZE(bvec))
 				break;
+			tail++;
 		}
 
 		if (!bc)