Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

splice: teach splice pipe reading about empty pipe buffers

Tetsuo Handa reports that splice() can return 0 before the real EOF, if
the data in the splice source pipe is an empty pipe buffer. That empty
pipe buffer case doesn't happen in any normal situation, but you can
trigger it by doing a write to a pipe that fails due to a page fault.

Tetsuo has a test-case to show the behavior:

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
const int fd = open("/tmp/testfile", O_WRONLY | O_CREAT, 0600);
int pipe_fd[2] = { -1, -1 };
pipe(pipe_fd);
write(pipe_fd[1], NULL, 4096);
/* This splice() should wait unless interrupted. */
return !splice(pipe_fd[0], NULL, fd, NULL, 65536, 0);
}

which results in

write(5, NULL, 4096) = -1 EFAULT (Bad address)
splice(4, NULL, 3, NULL, 65536, 0) = 0

and this can confuse splice() users into believing they have hit EOF
prematurely.

The issue was introduced when the pipe write code started pre-allocating
the pipe buffers before copying data from user space.

This is modified verion of Tetsuo's original patch.

Fixes: a194dfe6e6f6 ("pipe: Rearrange sequence in pipe_write() to preallocate slot")
Link:https://lore.kernel.org/linux-fsdevel/20201005121339.4063-1-penguin-kernel@I-love.SAKURA.ne.jp/
Reported-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Acked-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

+20
+20
fs/splice.c
··· 526 526 return 1; 527 527 } 528 528 529 + /* We know we have a pipe buffer, but maybe it's empty? */ 530 + static inline bool eat_empty_buffer(struct pipe_inode_info *pipe) 531 + { 532 + unsigned int tail = pipe->tail; 533 + unsigned int mask = pipe->ring_size - 1; 534 + struct pipe_buffer *buf = &pipe->bufs[tail & mask]; 535 + 536 + if (unlikely(!buf->len)) { 537 + pipe_buf_release(pipe, buf); 538 + pipe->tail = tail+1; 539 + return true; 540 + } 541 + 542 + return false; 543 + } 544 + 529 545 /** 530 546 * splice_from_pipe_next - wait for some data to splice from 531 547 * @pipe: pipe to splice from ··· 561 545 if (signal_pending(current)) 562 546 return -ERESTARTSYS; 563 547 548 + repeat: 564 549 while (pipe_empty(pipe->head, pipe->tail)) { 565 550 if (!pipe->writers) 566 551 return 0; ··· 582 565 583 566 pipe_wait_readable(pipe); 584 567 } 568 + 569 + if (eat_empty_buffer(pipe)) 570 + goto repeat; 585 571 586 572 return 1; 587 573 }