diff options
Diffstat (limited to 'test/syscalls/linux/pipe.cc')
-rw-r--r-- | test/syscalls/linux/pipe.cc | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc new file mode 100644 index 000000000..4731157e8 --- /dev/null +++ b/test/syscalls/linux/pipe.cc @@ -0,0 +1,480 @@ +// Copyright 2018 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <fcntl.h> /* Obtain O_* constant definitions */ +#include <sys/ioctl.h> +#include <sys/uio.h> +#include <unistd.h> + +#include <vector> + +#include "gtest/gtest.h" +#include "gtest/gtest.h" +#include "absl/strings/str_cat.h" +#include "absl/synchronization/notification.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "test/util/file_descriptor.h" +#include "test/util/test_util.h" +#include "test/util/thread_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Buffer size of a pipe. +// +// TODO: Get this from F_GETPIPE_SZ. +constexpr int kPipeSize = 65536; + +class PipeTest : public ::testing::Test { + public: + static void SetUpTestCase() { + // Tests intentionally generate SIGPIPE. + TEST_PCHECK(signal(SIGPIPE, SIG_IGN) != SIG_ERR); + } + + static void TearDownTestCase() { + TEST_PCHECK(signal(SIGPIPE, SIG_DFL) != SIG_ERR); + } +}; + +TEST_F(PipeTest, Basic) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int i = 0x12345678; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + // Ensure that the inode number is the same for each end. + struct stat rst; + ASSERT_THAT(fstat(fds[0], &rst), SyscallSucceeds()); + struct stat wst; + ASSERT_THAT(fstat(fds[1], &wst), SyscallSucceeds()); + EXPECT_EQ(rst.st_ino, wst.st_ino); + + ASSERT_THAT(write(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(read(fds[1], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + int j; + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + EXPECT_EQ(i, j); + + ASSERT_THAT(fcntl(fds[0], F_GETFL), SyscallSucceeds()); + ASSERT_THAT(fcntl(fds[1], F_GETFL), SyscallSucceedsWithValue(O_WRONLY)); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, BasicCloExec) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int i = 0x12345678; + ASSERT_THAT(pipe2(fds, O_CLOEXEC), SyscallSucceeds()); + + ASSERT_THAT(write(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(read(fds[1], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + int j; + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + EXPECT_EQ(i, j); + + ASSERT_THAT(fcntl(fds[0], F_GETFL), SyscallSucceeds()); + ASSERT_THAT(fcntl(fds[1], F_GETFL), SyscallSucceeds()); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, BasicNoBlock) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int i = 0x12345678; + ASSERT_THAT(pipe2(fds, O_NONBLOCK), SyscallSucceeds()); + + ASSERT_THAT(write(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(read(fds[1], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + + ASSERT_THAT(read(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EWOULDBLOCK)); + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + int j; + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + EXPECT_EQ(i, j); + ASSERT_THAT(read(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EWOULDBLOCK)); + + ASSERT_THAT(fcntl(fds[0], F_GETFL), SyscallSucceedsWithValue(O_NONBLOCK)); + ASSERT_THAT(fcntl(fds[1], F_GETFL), + SyscallSucceedsWithValue(O_NONBLOCK | O_WRONLY)); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, BasicBothOptions) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int i = 0x12345678; + ASSERT_THAT(pipe2(fds, O_NONBLOCK | O_CLOEXEC), SyscallSucceeds()); + + ASSERT_THAT(write(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(read(fds[1], &i, sizeof(i)), SyscallFailsWithErrno(EBADF)); + + ASSERT_THAT(read(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EWOULDBLOCK)); + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + int j; + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + EXPECT_EQ(i, j); + ASSERT_THAT(read(fds[0], &i, sizeof(i)), SyscallFailsWithErrno(EWOULDBLOCK)); + + ASSERT_THAT(fcntl(fds[0], F_GETFL), SyscallSucceedsWithValue(O_NONBLOCK)); + ASSERT_THAT(fcntl(fds[1], F_GETFL), + SyscallSucceedsWithValue(O_NONBLOCK | O_WRONLY)); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, BasicBadOptions) { + int fds[2]; + ASSERT_THAT(pipe2(fds, 0xDEAD), SyscallFailsWithErrno(EINVAL)); +} + +TEST_F(PipeTest, Seek) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int i = 0x12345678; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + ASSERT_THAT(lseek(fds[0], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[0], 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[0], 4, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 4, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + + ASSERT_THAT(lseek(fds[0], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[0], 4, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 4, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + int j; + + ASSERT_THAT(lseek(fds[0], 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[0], 4, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 0, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 4, SEEK_SET), SyscallFailsWithErrno(ESPIPE)); + + ASSERT_THAT(lseek(fds[0], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[0], 4, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 0, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + ASSERT_THAT(lseek(fds[1], 4, SEEK_CUR), SyscallFailsWithErrno(ESPIPE)); + + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + EXPECT_EQ(i, j); + + ASSERT_THAT(fcntl(fds[0], F_GETFL), SyscallSucceeds()); + ASSERT_THAT(fcntl(fds[1], F_GETFL), SyscallSucceedsWithValue(O_WRONLY)); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, AbsoluteOffsetSyscallsFail) { + // Syscalls for IO at absolute offsets fail because pipes are not seekable. + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + std::vector<char> buf(4096); + struct iovec iov; + + EXPECT_THAT(pread(fds[1], buf.data(), buf.size(), 0), + SyscallFailsWithErrno(ESPIPE)); + EXPECT_THAT(pwrite(fds[0], buf.data(), buf.size(), 0), + SyscallFailsWithErrno(ESPIPE)); + EXPECT_THAT(preadv(fds[1], &iov, 1, 0), SyscallFailsWithErrno(ESPIPE)); + EXPECT_THAT(pwritev(fds[0], &iov, 1, 0), SyscallFailsWithErrno(ESPIPE)); + + EXPECT_THAT(close(fds[0]), SyscallSucceeds()); + EXPECT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, WriterSideCloses) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + int rfd = fds[0]; + int i = 123; + ScopedThread t([rfd]() { + int j; + ASSERT_THAT(read(rfd, &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + // This will return when the close() completes. + ASSERT_THAT(read(rfd, &j, sizeof(j)), SyscallSucceeds()); + // This will return straight away. + ASSERT_THAT(read(rfd, &j, sizeof(j)), SyscallSucceeds()); + }); + // Sleep a bit so the thread can block. + absl::SleepFor(absl::Seconds(1.0)); + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + // Sleep a bit so the thread can block again. + absl::SleepFor(absl::Seconds(3.0)); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); + t.Join(); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); +} + +TEST_F(PipeTest, WriterSideClosesReadDataFirst) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + int i = 123; + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); + int j; + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceedsWithValue(sizeof(j))); + ASSERT_EQ(j, i); + ASSERT_THAT(read(fds[0], &j, sizeof(j)), SyscallSucceeds()); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); +} + +TEST_F(PipeTest, ReaderSideCloses) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + int i = 123; + ASSERT_THAT(write(fds[1], &i, sizeof(i)), SyscallFailsWithErrno(EPIPE)); + + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, CloseTwice) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); + ASSERT_THAT(close(fds[0]), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(close(fds[1]), SyscallFailsWithErrno(EBADF)); + + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + ASSERT_THAT(close(fds[0]), SyscallFailsWithErrno(EBADF)); + ASSERT_THAT(close(fds[1]), SyscallFailsWithErrno(EBADF)); +} + +// Blocking write returns EPIPE when read end is closed if nothing has been +// written. +TEST_F(PipeTest, BlockWriteClosed) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + int wfd = fds[1]; + + absl::Notification notify; + ScopedThread t([wfd, ¬ify]() { + std::vector<char> buf(kPipeSize); + // Exactly fill the pipe buffer. + ASSERT_THAT(WriteFd(wfd, buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + + notify.Notify(); + + // Attempt to write one more byte. Blocks. + // N.B. Don't use WriteFd, we don't want a retry. + ASSERT_THAT(write(wfd, buf.data(), 1), SyscallFailsWithErrno(EPIPE)); + }); + + notify.WaitForNotification(); + absl::SleepFor(absl::Seconds(1.0)); + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + + t.Join(); + + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +// Blocking write returns EPIPE when read end is closed even if something has +// been written. +// +// FIXME: Pipe writes blocking early allows S/R to interrupt the +// write(2) call before the buffer is full. Then the next call will will return +// non-zero instead of EPIPE. +TEST_F(PipeTest, BlockPartialWriteClosed_NoRandomSave) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + int wfd = fds[1]; + + ScopedThread t([wfd]() { + std::vector<char> buf(2 * kPipeSize); + // Write more than fits in the buffer. Blocks then returns partial write + // when the other end is closed. The next call returns EPIPE. + if (IsRunningOnGvisor()) { + // FIXME: Pipe writes block early on gVisor, resulting in a + // shorter than expected partial write. + ASSERT_THAT(write(wfd, buf.data(), buf.size()), + SyscallSucceedsWithValue(::testing::Gt(0))); + } else { + ASSERT_THAT(write(wfd, buf.data(), buf.size()), + SyscallSucceedsWithValue(kPipeSize)); + } + ASSERT_THAT(write(wfd, buf.data(), buf.size()), + SyscallFailsWithErrno(EPIPE)); + }); + + // Leave time for write to become blocked. + absl::SleepFor(absl::Seconds(1.0)); + + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + + t.Join(); + + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, ReadFromClosedFd_NoRandomSave) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + int rfd = fds[0]; + absl::Notification notify; + ScopedThread t([rfd, ¬ify]() { + int f; + notify.Notify(); + ASSERT_THAT(read(rfd, &f, sizeof(f)), SyscallSucceedsWithValue(sizeof(f))); + ASSERT_EQ(123, f); + }); + notify.WaitForNotification(); + // Make sure that the thread gets to read(). + absl::SleepFor(absl::Seconds(5.0)); + { + // We cannot save/restore here as the read end of pipe is closed but there + // is ongoing read() above. We will not be able to restart the read() + // successfully in restore run since the read fd is closed. + const DisableSave ds; + ASSERT_THAT(close(fds[0]), SyscallSucceeds()); + int i = 123; + ASSERT_THAT(write(fds[1], &i, sizeof(i)), + SyscallSucceedsWithValue(sizeof(i))); + t.Join(); + } + ASSERT_THAT(close(fds[1]), SyscallSucceeds()); +} + +TEST_F(PipeTest, FionRead) { + // fds[0] is read end, fds[1] is write end. + int fds[2]; + int data[2] = {0x12345678, 0x9101112}; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + + int n = -1; + EXPECT_THAT(ioctl(fds[0], FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + n = -1; + EXPECT_THAT(ioctl(fds[1], FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, 0); + + EXPECT_THAT(write(fds[1], data, sizeof(data)), + SyscallSucceedsWithValue(sizeof(data))); + + n = -1; + EXPECT_THAT(ioctl(fds[0], FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, sizeof(data)); + n = -1; + EXPECT_THAT(ioctl(fds[1], FIONREAD, &n), SyscallSucceedsWithValue(0)); + EXPECT_EQ(n, sizeof(data)); +} + +// Test that opening an empty anonymous pipe RDONLY via /proc/self/fd/N does not +// block waiting for a writer. +TEST_F(PipeTest, OpenViaProcSelfFD) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + // Close the write end of the pipe. + wfd.release(); + + // Open other side via /proc/self/fd. It should not block. + FileDescriptor proc_self_fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(absl::StrCat("/proc/self/fd/", fds[0]), O_RDONLY)); +} + +// Test that opening and reading from an anonymous pipe (with existing writes) +// RDONLY via /proc/self/fd/N returns the existing data. +TEST_F(PipeTest, OpenViaProcSelfFDWithWrites) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + // Write to the pipe and then close the write fd. + char data = 'x'; + ASSERT_THAT(write(fds[1], &data, 1), SyscallSucceedsWithValue(1)); + wfd.release(); + + // Open read side via /proc/self/fd, and read from it. + FileDescriptor proc_self_fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(absl::StrCat("/proc/self/fd/", fds[0]), O_RDONLY)); + char got; + ASSERT_THAT(read(proc_self_fd.get(), &got, 1), SyscallSucceedsWithValue(1)); + + // We should get what we sent. + EXPECT_EQ(got, data); +} + +TEST_F(PipeTest, LargeFile) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + int rflags; + EXPECT_THAT(rflags = fcntl(rfd.get(), F_GETFL), SyscallSucceeds()); + + // The kernel did *not* set O_LARGEFILE. + EXPECT_EQ(rflags, 0); +} + +// Test that accessing /proc/<PID>/fd/<FD> correctly decrements the refcount of +// that file descriptor. +TEST_F(PipeTest, ProcFDReleasesFile) { + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + // Stat the pipe FD, which shouldn't alter the refcount of the write end of + // the pipe. + struct stat wst; + ASSERT_THAT(lstat(absl::StrCat("/proc/self/fd/", wfd.get()).c_str(), &wst), + SyscallSucceeds()); + + // Close the write end of the pipe and ensure that read indicates EOF. + wfd.reset(); + char buf; + ASSERT_THAT(read(rfd.get(), &buf, 1), SyscallSucceedsWithValue(0)); +} + +} // namespace +} // namespace testing +} // namespace gvisor |