diff --git a/g3doc/user_guide/compatibility.md b/g3doc/user_guide/compatibility.md index ef50c0147..a10f5c940 100644 --- a/g3doc/user_guide/compatibility.md +++ b/g3doc/user_guide/compatibility.md @@ -67,7 +67,7 @@ Most common utilities work. Note that: | gcore | Working. | | gdb | Working. | | gosu | Working. | -| grep | Working (unless stdin is a pipe and stdout is /dev/null). | +| grep | Working. | | ifconfig | Works partially, like ip. Full support [in progress](https://gvisor.dev/issue/578). | | ip | Some subcommands work (e.g. addr, route). Full support [in progress](https://gvisor.dev/issue/578). | | less | Working. | diff --git a/pkg/sentry/kernel/pipe/vfs.go b/pkg/sentry/kernel/pipe/vfs.go index 3d58249b7..32527e045 100644 --- a/pkg/sentry/kernel/pipe/vfs.go +++ b/pkg/sentry/kernel/pipe/vfs.go @@ -277,6 +277,13 @@ func (fd *VFSPipeFD) SpliceToNonPipe(ctx context.Context, out *vfs.FileDescripti fd.pipe.consumeLocked(n) } + // Implementations of out.[P]Write() that ignore written data (e.g. + // /dev/null) may skip calling src.CopyIn[To]() and therefore miss getting + // ErrWouldBlock from Pipe.peekLocked(). + if n == 0 && err == nil && fd.pipe.size == 0 && fd.pipe.HasWriters() { + err = linuxerr.ErrWouldBlock + } + fd.pipe.mu.Unlock() if n > 0 { diff --git a/test/syscalls/linux/splice.cc b/test/syscalls/linux/splice.cc index f49ea4a7a..4d8e6cc73 100644 --- a/test/syscalls/linux/splice.cc +++ b/test/syscalls/linux/splice.cc @@ -921,6 +921,19 @@ TEST(SpliceTest, FromPipeWithConcurrentIo) { } } +// Regression test for #9736. +TEST(SpliceTest, FromEmptyPipeWithWriterToDevNull) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + const FileDescriptor rfd(fds[0]); + const FileDescriptor wfd(fds[1]); + const FileDescriptor out_fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/null", O_WRONLY)); + ASSERT_THAT( + splice(rfd.get(), nullptr, out_fd.get(), nullptr, 1, SPLICE_F_NONBLOCK), + SyscallFailsWithErrno(EAGAIN)); +} + } // namespace } // namespace testing