// 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 #include #include #include #include #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(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(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(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(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(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 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 buf(kReadvTestDataSize); struct iovec iov[2]; iov[0].iov_base = reinterpret_cast(~static_cast(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)); } TEST_F(ReadvTest, ReadvWithOpath) { SKIP_IF(IsRunningWithVFS1()); char buffer[1024]; struct iovec iov[1]; iov[0].iov_base = buffer; iov[0].iov_len = 1024; TempPath tmpfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfile.path().c_str(), O_PATH)); ASSERT_THAT(readv(fd.get(), iov, 1), SyscallFailsWithErrno(EBADF)); } // This test depends on the maximum extent of a single readv() syscall, so // we can't tolerate interruption from saving. TEST(ReadvTestNoFixture, TruncatedAtMax) { // 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 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