Skip to content

Commit d1a819a

Browse files
committed
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: a194dfe ("pipe: Rearrange sequence in pipe_write() to preallocate slot") Link:https://lore.kernel.org/linux-fsdevel/[email protected]/ Reported-by: Tetsuo Handa <[email protected]> Acked-by: Tetsuo Handa <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 7575fdd commit d1a819a

File tree

1 file changed

+20
-0
lines changed

1 file changed

+20
-0
lines changed

fs/splice.c

+20
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,22 @@ static int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_des
526526
return 1;
527527
}
528528

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+
529545
/**
530546
* splice_from_pipe_next - wait for some data to splice from
531547
* @pipe: pipe to splice from
@@ -545,6 +561,7 @@ static int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_des
545561
if (signal_pending(current))
546562
return -ERESTARTSYS;
547563

564+
repeat:
548565
while (pipe_empty(pipe->head, pipe->tail)) {
549566
if (!pipe->writers)
550567
return 0;
@@ -566,6 +583,9 @@ static int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_des
566583
pipe_wait_readable(pipe);
567584
}
568585

586+
if (eat_empty_buffer(pipe))
587+
goto repeat;
588+
569589
return 1;
570590
}
571591

0 commit comments

Comments
 (0)