diff options
Diffstat (limited to 'test/syscalls/linux/partial_bad_buffer.cc')
-rw-r--r-- | test/syscalls/linux/partial_bad_buffer.cc | 405 |
1 files changed, 0 insertions, 405 deletions
diff --git a/test/syscalls/linux/partial_bad_buffer.cc b/test/syscalls/linux/partial_bad_buffer.cc deleted file mode 100644 index df7129acc..000000000 --- a/test/syscalls/linux/partial_bad_buffer.cc +++ /dev/null @@ -1,405 +0,0 @@ -// 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 <netinet/in.h> -#include <netinet/tcp.h> -#include <sys/mman.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/fs_util.h" -#include "test/util/posix_error.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -using ::testing::Gt; - -namespace gvisor { -namespace testing { - -namespace { - -constexpr char kMessage[] = "hello world"; - -// PartialBadBufferTest checks the result of various IO syscalls when passed a -// buffer that does not have the space specified in the syscall (most of it is -// PROT_NONE). Linux is annoyingly inconsistent among different syscalls, so we -// test all of them. -class PartialBadBufferTest : public ::testing::Test { - protected: - void SetUp() override { - // Create and open a directory for getdents cases. - directory_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - ASSERT_THAT( - directory_fd_ = open(directory_.path().c_str(), O_RDONLY | O_DIRECTORY), - SyscallSucceeds()); - - // Create and open a normal file, placing it in the directory - // so the getdents cases have some dirents. - name_ = JoinPath(directory_.path(), "a"); - ASSERT_THAT(fd_ = open(name_.c_str(), O_RDWR | O_CREAT, 0644), - SyscallSucceeds()); - - // Write some initial data. - size_t size = sizeof(kMessage) - 1; - EXPECT_THAT(WriteFd(fd_, &kMessage, size), SyscallSucceedsWithValue(size)); - ASSERT_THAT(lseek(fd_, 0, SEEK_SET), SyscallSucceeds()); - - // Map a useable buffer. - addr_ = mmap(0, 2 * kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - ASSERT_NE(addr_, MAP_FAILED); - char* buf = reinterpret_cast<char*>(addr_); - - // Guard page for our read to run into. - ASSERT_THAT(mprotect(reinterpret_cast<void*>(buf + kPageSize), kPageSize, - PROT_NONE), - SyscallSucceeds()); - - // Leave only one free byte in the buffer. - bad_buffer_ = buf + kPageSize - 1; - } - - off_t Size() { - struct stat st; - int rc = fstat(fd_, &st); - if (rc < 0) { - return static_cast<off_t>(rc); - } - return st.st_size; - } - - void TearDown() override { - EXPECT_THAT(munmap(addr_, 2 * kPageSize), SyscallSucceeds()) << addr_; - EXPECT_THAT(close(fd_), SyscallSucceeds()); - EXPECT_THAT(unlink(name_.c_str()), SyscallSucceeds()); - EXPECT_THAT(close(directory_fd_), SyscallSucceeds()); - } - - // Return buffer with n bytes of free space. - // N.B. this is the same buffer used to back bad_buffer_. - char* FreeBytes(size_t n) { - TEST_CHECK(n <= static_cast<size_t>(4096)); - return reinterpret_cast<char*>(addr_) + kPageSize - n; - } - - std::string name_; - int fd_; - TempPath directory_; - int directory_fd_; - void* addr_; - char* bad_buffer_; -}; - -// We do both "big" and "small" tests to try to hit the "zero copy" and -// non-"zero copy" paths, which have different code paths for handling faults. - -TEST_F(PartialBadBufferTest, ReadBig) { - EXPECT_THAT(RetryEINTR(read)(fd_, bad_buffer_, kPageSize), - SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, ReadSmall) { - EXPECT_THAT(RetryEINTR(read)(fd_, bad_buffer_, 10), - SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, PreadBig) { - EXPECT_THAT(RetryEINTR(pread)(fd_, bad_buffer_, kPageSize, 0), - SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, PreadSmall) { - EXPECT_THAT(RetryEINTR(pread)(fd_, bad_buffer_, 10, 0), - SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, ReadvBig) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = kPageSize; - - EXPECT_THAT(RetryEINTR(readv)(fd_, &vec, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, ReadvSmall) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = 10; - - EXPECT_THAT(RetryEINTR(readv)(fd_, &vec, 1), SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, PreadvBig) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = kPageSize; - - EXPECT_THAT(RetryEINTR(preadv)(fd_, &vec, 1, 0), SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, PreadvSmall) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = 10; - - EXPECT_THAT(RetryEINTR(preadv)(fd_, &vec, 1, 0), SyscallSucceedsWithValue(1)); - EXPECT_EQ('h', bad_buffer_[0]); -} - -TEST_F(PartialBadBufferTest, WriteBig) { - off_t orig_size = Size(); - int n; - - ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT( - (n = RetryEINTR(write)(fd_, bad_buffer_, kPageSize)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, WriteSmall) { - off_t orig_size = Size(); - int n; - - ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT( - (n = RetryEINTR(write)(fd_, bad_buffer_, 10)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, PwriteBig) { - off_t orig_size = Size(); - int n; - - EXPECT_THAT( - (n = RetryEINTR(pwrite)(fd_, bad_buffer_, kPageSize, orig_size)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, PwriteSmall) { - off_t orig_size = Size(); - int n; - - EXPECT_THAT( - (n = RetryEINTR(pwrite)(fd_, bad_buffer_, 10, orig_size)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, WritevBig) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = kPageSize; - off_t orig_size = Size(); - int n; - - ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT( - (n = RetryEINTR(writev)(fd_, &vec, 1)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, WritevSmall) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = 10; - off_t orig_size = Size(); - int n; - - ASSERT_THAT(lseek(fd_, orig_size, SEEK_SET), SyscallSucceeds()); - EXPECT_THAT( - (n = RetryEINTR(writev)(fd_, &vec, 1)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, PwritevBig) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = kPageSize; - off_t orig_size = Size(); - int n; - - EXPECT_THAT( - (n = RetryEINTR(pwritev)(fd_, &vec, 1, orig_size)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -TEST_F(PartialBadBufferTest, PwritevSmall) { - struct iovec vec; - vec.iov_base = bad_buffer_; - vec.iov_len = 10; - off_t orig_size = Size(); - int n; - - EXPECT_THAT( - (n = RetryEINTR(pwritev)(fd_, &vec, 1, orig_size)), - AnyOf(SyscallFailsWithErrno(EFAULT), SyscallSucceedsWithValue(1))); - EXPECT_EQ(Size(), orig_size + (n >= 0 ? n : 0)); -} - -// getdents returns EFAULT when the you claim the buffer is large enough, but -// it actually isn't. -TEST_F(PartialBadBufferTest, GetdentsBig) { - EXPECT_THAT(RetryEINTR(syscall)(SYS_getdents64, directory_fd_, bad_buffer_, - kPageSize), - SyscallFailsWithErrno(EFAULT)); -} - -// getdents returns EINVAL when the you claim the buffer is too small. -TEST_F(PartialBadBufferTest, GetdentsSmall) { - EXPECT_THAT( - RetryEINTR(syscall)(SYS_getdents64, directory_fd_, bad_buffer_, 10), - SyscallFailsWithErrno(EINVAL)); -} - -// getdents will write entries into a buffer if there is space before it faults. -TEST_F(PartialBadBufferTest, GetdentsOneEntry) { - // 30 bytes is enough for one (small) entry. - char* buf = FreeBytes(30); - - EXPECT_THAT( - RetryEINTR(syscall)(SYS_getdents64, directory_fd_, buf, kPageSize), - SyscallSucceedsWithValue(Gt(0))); -} - -PosixErrorOr<sockaddr_storage> InetLoopbackAddr(int family) { - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - addr.ss_family = family; - switch (family) { - case AF_INET: - reinterpret_cast<struct sockaddr_in*>(&addr)->sin_addr.s_addr = - htonl(INADDR_LOOPBACK); - break; - case AF_INET6: - reinterpret_cast<struct sockaddr_in6*>(&addr)->sin6_addr = - in6addr_loopback; - break; - default: - return PosixError(EINVAL, - absl::StrCat("unknown socket family: ", family)); - } - return addr; -} - -// SendMsgTCP verifies that calling sendmsg with a bad address returns an -// EFAULT. It also verifies that passing a buffer which is made up of 2 -// pages one valid and one guard page succeeds as long as the write is -// for exactly the size of 1 page. -TEST_F(PartialBadBufferTest, SendMsgTCP) { - auto listen_socket = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - - // Initialize address to the loopback one. - sockaddr_storage addr = ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(AF_INET)); - socklen_t addrlen = sizeof(addr); - - // Bind to some port then start listening. - ASSERT_THAT(bind(listen_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - ASSERT_THAT(listen(listen_socket.get(), SOMAXCONN), SyscallSucceeds()); - - // Get the address we're listening on, then connect to it. We need to do this - // because we're allowing the stack to pick a port for us. - ASSERT_THAT(getsockname(listen_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - auto send_socket = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - - ASSERT_THAT( - RetryEINTR(connect)(send_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - // Accept the connection. - auto recv_socket = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_socket.get(), nullptr, nullptr)); - - // TODO(gvisor.dev/issue/674): Update this once Netstack matches linux - // behaviour on a setsockopt of SO_RCVBUF/SO_SNDBUF. - // - // Set SO_SNDBUF for socket to exactly kPageSize+1. - // - // gVisor does not double the value passed in SO_SNDBUF like linux does so we - // just increase it by 1 byte here for gVisor so that we can test writing 1 - // byte past the valid page and check that it triggers an EFAULT - // correctly. Otherwise in gVisor the sendmsg call will just return with no - // error with kPageSize bytes written successfully. - const uint32_t buf_size = kPageSize + 1; - ASSERT_THAT(setsockopt(send_socket.get(), SOL_SOCKET, SO_SNDBUF, &buf_size, - sizeof(buf_size)), - SyscallSucceedsWithValue(0)); - - struct msghdr hdr = {}; - struct iovec iov = {}; - iov.iov_base = bad_buffer_; - iov.iov_len = kPageSize; - hdr.msg_iov = &iov; - hdr.msg_iovlen = 1; - - ASSERT_THAT(RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0), - SyscallFailsWithErrno(EFAULT)); - - // Now assert that writing kPageSize from addr_ succeeds. - iov.iov_base = addr_; - ASSERT_THAT(RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0), - SyscallSucceedsWithValue(kPageSize)); - // Read all the data out so that we drain the socket SND_BUF on the sender. - std::vector<char> buffer(kPageSize); - ASSERT_THAT(RetryEINTR(read)(recv_socket.get(), buffer.data(), kPageSize), - SyscallSucceedsWithValue(kPageSize)); - - // Sleep for a shortwhile to ensure that we have time to process the - // ACKs. This is not strictly required unless running under gotsan which is a - // lot slower and can result in the next write to write only 1 byte instead of - // our intended kPageSize + 1. - absl::SleepFor(absl::Milliseconds(50)); - - // Now assert that writing > kPageSize results in EFAULT. - iov.iov_len = kPageSize + 1; - ASSERT_THAT(RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0), - SyscallFailsWithErrno(EFAULT)); -} - -} // namespace - -} // namespace testing -} // namespace gvisor |