diff options
Diffstat (limited to 'test/syscalls/linux/readv.cc')
-rw-r--r-- | test/syscalls/linux/readv.cc | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/test/syscalls/linux/readv.cc b/test/syscalls/linux/readv.cc new file mode 100644 index 000000000..baaf9f757 --- /dev/null +++ b/test/syscalls/linux/readv.cc @@ -0,0 +1,294 @@ +// Copyright 2018 The gVisor Authors. +// +// 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 <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/types.h> +#include <unistd.h> + +#include "gtest/gtest.h" +#include "test/syscalls/linux/file_base.h" +#include "test/syscalls/linux/readv_common.h" +#include "test/util/file_descriptor.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/timer_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class ReadvTest : public FileTest { + void SetUp() override { + FileTest::SetUp(); + + ASSERT_THAT(write(test_file_fd_.get(), kReadvTestData, kReadvTestDataSize), + SyscallSucceedsWithValue(kReadvTestDataSize)); + ASSERT_THAT(lseek(test_file_fd_.get(), 0, SEEK_SET), + SyscallSucceedsWithValue(0)); + ASSERT_THAT(write(test_pipe_[1], kReadvTestData, kReadvTestDataSize), + SyscallSucceedsWithValue(kReadvTestDataSize)); + } +}; + +TEST_F(ReadvTest, ReadOneBufferPerByte_File) { + ReadOneBufferPerByte(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadOneBufferPerByte_Pipe) { + ReadOneBufferPerByte(test_pipe_[0]); +} + +TEST_F(ReadvTest, ReadOneHalfAtATime_File) { + ReadOneHalfAtATime(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadOneHalfAtATime_Pipe) { + ReadOneHalfAtATime(test_pipe_[0]); +} + +TEST_F(ReadvTest, ReadAllOneBuffer_File) { + ReadAllOneBuffer(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadAllOneBuffer_Pipe) { ReadAllOneBuffer(test_pipe_[0]); } + +TEST_F(ReadvTest, ReadAllOneLargeBuffer_File) { + ReadAllOneLargeBuffer(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadAllOneLargeBuffer_Pipe) { + ReadAllOneLargeBuffer(test_pipe_[0]); +} + +TEST_F(ReadvTest, ReadBuffersOverlapping_File) { + ReadBuffersOverlapping(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadBuffersOverlapping_Pipe) { + ReadBuffersOverlapping(test_pipe_[0]); +} + +TEST_F(ReadvTest, ReadBuffersDiscontinuous_File) { + ReadBuffersDiscontinuous(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadBuffersDiscontinuous_Pipe) { + ReadBuffersDiscontinuous(test_pipe_[0]); +} + +TEST_F(ReadvTest, ReadIovecsCompletelyFilled_File) { + ReadIovecsCompletelyFilled(test_file_fd_.get()); +} + +TEST_F(ReadvTest, ReadIovecsCompletelyFilled_Pipe) { + ReadIovecsCompletelyFilled(test_pipe_[0]); +} + +TEST_F(ReadvTest, BadFileDescriptor) { + char buffer[1024]; + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = 1024; + + ASSERT_THAT(readv(-1, iov, 1024), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(ReadvTest, BadIovecsPointer_File) { + ASSERT_THAT(readv(test_file_fd_.get(), nullptr, 1), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvTest, BadIovecsPointer_Pipe) { + ASSERT_THAT(readv(test_pipe_[0], nullptr, 1), SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvTest, BadIovecBase_File) { + struct iovec iov[1]; + iov[0].iov_base = nullptr; + iov[0].iov_len = 1024; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), + SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvTest, BadIovecBase_Pipe) { + struct iovec iov[1]; + iov[0].iov_base = nullptr; + iov[0].iov_len = 1024; + ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallFailsWithErrno(EFAULT)); +} + +TEST_F(ReadvTest, ZeroIovecs_File) { + struct iovec iov[1]; + iov[0].iov_base = 0; + iov[0].iov_len = 0; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), SyscallSucceeds()); +} + +TEST_F(ReadvTest, ZeroIovecs_Pipe) { + struct iovec iov[1]; + iov[0].iov_base = 0; + iov[0].iov_len = 0; + ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallSucceeds()); +} + +TEST_F(ReadvTest, NotReadable_File) { + char buffer[1024]; + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = 1024; + + std::string wronly_file = NewTempAbsPath(); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(wronly_file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR)); + ASSERT_THAT(readv(fd.get(), iov, 1), SyscallFailsWithErrno(EBADF)); + fd.reset(); // Close before unlinking. + ASSERT_THAT(unlink(wronly_file.c_str()), SyscallSucceeds()); +} + +TEST_F(ReadvTest, NotReadable_Pipe) { + char buffer[1024]; + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = 1024; + ASSERT_THAT(readv(test_pipe_[1], iov, 1), SyscallFailsWithErrno(EBADF)); +} + +TEST_F(ReadvTest, DirNotReadable) { + char buffer[1024]; + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = 1024; + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), O_RDONLY)); + ASSERT_THAT(readv(fd.get(), iov, 1), SyscallFailsWithErrno(EISDIR)); +} + +TEST_F(ReadvTest, OffsetIncremented) { + char* buffer = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = kReadvTestDataSize; + + ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), + SyscallSucceedsWithValue(kReadvTestDataSize)); + ASSERT_THAT(lseek(test_file_fd_.get(), 0, SEEK_CUR), + SyscallSucceedsWithValue(kReadvTestDataSize)); + + free(buffer); +} + +TEST_F(ReadvTest, EndOfFile) { + char* buffer = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); + struct iovec iov[1]; + iov[0].iov_base = buffer; + iov[0].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), + SyscallSucceedsWithValue(kReadvTestDataSize)); + free(buffer); + + buffer = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); + iov[0].iov_base = buffer; + iov[0].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 1), SyscallSucceedsWithValue(0)); + free(buffer); +} + +TEST_F(ReadvTest, WouldBlock_Pipe) { + struct iovec iov[1]; + iov[0].iov_base = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); + iov[0].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_pipe_[0], iov, 1), + SyscallSucceedsWithValue(kReadvTestDataSize)); + free(iov[0].iov_base); + + iov[0].iov_base = reinterpret_cast<char*>(malloc(kReadvTestDataSize)); + ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallFailsWithErrno(EAGAIN)); + free(iov[0].iov_base); +} + +TEST_F(ReadvTest, ZeroBuffer) { + char buf[10]; + struct iovec iov[1]; + iov[0].iov_base = buf; + iov[0].iov_len = 0; + ASSERT_THAT(readv(test_pipe_[0], iov, 1), SyscallSucceedsWithValue(0)); +} + +TEST_F(ReadvTest, NullIovecInNonemptyArray) { + std::vector<char> buf(kReadvTestDataSize); + struct iovec iov[2]; + iov[0].iov_base = nullptr; + iov[0].iov_len = 0; + iov[1].iov_base = buf.data(); + iov[1].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 2), + SyscallSucceedsWithValue(kReadvTestDataSize)); +} + +TEST_F(ReadvTest, IovecOutsideTaskAddressRangeInNonemptyArray) { + std::vector<char> buf(kReadvTestDataSize); + struct iovec iov[2]; + iov[0].iov_base = reinterpret_cast<void*>(~static_cast<uintptr_t>(0)); + iov[0].iov_len = 0; + iov[1].iov_base = buf.data(); + iov[1].iov_len = kReadvTestDataSize; + ASSERT_THAT(readv(test_file_fd_.get(), iov, 2), + SyscallFailsWithErrno(EFAULT)); +} + +// This test depends on the maximum extent of a single readv() syscall, so +// we can't tolerate interruption from saving. +TEST(ReadvTestNoFixture, TruncatedAtMax_NoRandomSave) { + // Ensure that we won't be interrupted by ITIMER_PROF. This is particularly + // important in environments where automated profiling tools may start + // ITIMER_PROF automatically. + struct itimerval itv = {}; + auto const cleanup_itimer = + ASSERT_NO_ERRNO_AND_VALUE(ScopedItimer(ITIMER_PROF, itv)); + + // From Linux's include/linux/fs.h. + size_t const MAX_RW_COUNT = INT_MAX & ~(kPageSize - 1); + + // Create an iovec array with 3 segments pointing to consecutive parts of a + // buffer. The first covers all but the last three pages, and should be + // written to in its entirety. The second covers the last page before + // MAX_RW_COUNT and the first page after; only the first page should be + // written to. The third covers the last page of the buffer, and should be + // skipped entirely. + size_t const kBufferSize = MAX_RW_COUNT + 2 * kPageSize; + size_t const kFirstOffset = MAX_RW_COUNT - kPageSize; + size_t const kSecondOffset = MAX_RW_COUNT + kPageSize; + // The buffer is too big to fit on the stack. + std::vector<char> buf(kBufferSize); + struct iovec iov[3]; + iov[0].iov_base = buf.data(); + iov[0].iov_len = kFirstOffset; + iov[1].iov_base = buf.data() + kFirstOffset; + iov[1].iov_len = kSecondOffset - kFirstOffset; + iov[2].iov_base = buf.data() + kSecondOffset; + iov[2].iov_len = kBufferSize - kSecondOffset; + + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY)); + EXPECT_THAT(readv(fd.get(), iov, 3), SyscallSucceedsWithValue(MAX_RW_COUNT)); +} + +} // namespace + +} // namespace testing +} // namespace gvisor |