gvisor/test/syscalls/linux/readv.cc

294 lines
8.9 KiB
C++

// 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 "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.
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