// 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 <linux/magic.h>
#include <linux/unistd.h>
#include <setjmp.h>  // IWYU pragma: keep
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/statfs.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <limits>
#include <vector>

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
#include "absl/strings/str_split.h"
#include "test/util/cleanup.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
#include "test/util/logging.h"
#include "test/util/memory_util.h"
#include "test/util/multiprocess_util.h"
#include "test/util/posix_error.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"

using ::testing::AnyOf;
using ::testing::Eq;
using ::testing::Gt;

namespace gvisor {
namespace testing {

namespace {

static sigjmp_buf jmpbuf;
static volatile int si_code_received;

void MprotectProtNoneHandler(int sig, siginfo_t* info, void* ctx) {
  si_code_received = info->si_code;
  siglongjmp(jmpbuf, 1);
}

PosixErrorOr<int64_t> VirtualMemorySize() {
  ASSIGN_OR_RETURN_ERRNO(auto contents, GetContents("/proc/self/statm"));
  std::vector<std::string> parts = absl::StrSplit(contents, ' ');
  if (parts.empty()) {
    return PosixError(EINVAL, "Unable to parse /proc/self/statm");
  }
  ASSIGN_OR_RETURN_ERRNO(auto pages, Atoi<int64_t>(parts[0]));
  return pages * getpagesize();
}

class MMapTest : public ::testing::Test {
 protected:
  // Unmap mapping, if one was made.
  void TearDown() override {
    if (addr_) {
      EXPECT_THAT(Unmap(), SyscallSucceeds());
    }
  }

  // Remembers mapping, so it can be automatically unmapped.
  uintptr_t Map(uintptr_t addr, size_t length, int prot, int flags, int fd,
                off_t offset) {
    void* ret =
        mmap(reinterpret_cast<void*>(addr), length, prot, flags, fd, offset);

    if (ret != MAP_FAILED) {
      addr_ = ret;
      length_ = length;
    }

    return reinterpret_cast<uintptr_t>(ret);
  }

  // Unmap previous mapping
  int Unmap() {
    if (!addr_) {
      return -1;
    }

    int ret = MunmapSafe(addr_, length_);

    addr_ = nullptr;
    length_ = 0;

    return ret;
  }

  // Msync the mapping.
  int Msync() { return msync(addr_, length_, MS_SYNC); }

  // Mlock the mapping.
  int Mlock() { return mlock(addr_, length_); }

  // Munlock the mapping.
  int Munlock() { return munlock(addr_, length_); }

  int Protect(uintptr_t addr, size_t length, int prot) {
    return mprotect(reinterpret_cast<void*>(addr), length, prot);
  }

  void* addr_ = nullptr;
  size_t length_ = 0;
};

// Matches if arg contains the same contents as string str.
MATCHER_P(EqualsMemory, str, "") {
  if (0 == memcmp(arg, str.c_str(), str.size())) {
    return true;
  }

  *result_listener << "Memory did not match. Got:\n"
                   << absl::BytesToHexString(
                          std::string(static_cast<char*>(arg), str.size()))
                   << "Want:\n"
                   << absl::BytesToHexString(str);
  return false;
}

// We can't map pipes, but for different reasons.
TEST_F(MMapTest, MapPipe) {
  int fds[2];
  ASSERT_THAT(pipe(fds), SyscallSucceeds());
  EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fds[0], 0),
              SyscallFailsWithErrno(ENODEV));
  EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fds[1], 0),
              SyscallFailsWithErrno(EACCES));
  ASSERT_THAT(close(fds[0]), SyscallSucceeds());
  ASSERT_THAT(close(fds[1]), SyscallSucceeds());
}

// It's very common to mmap /dev/zero because anonymous mappings aren't part
// of POSIX although they are widely supported. So a zero initialized memory
// region would actually come from a "file backed" /dev/zero mapping.
TEST_F(MMapTest, MapDevZeroShared) {
  // This test will verify that we're able to map a page backed by /dev/zero
  // as MAP_SHARED.
  const FileDescriptor dev_zero =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR));

  // Test that we can create a RW SHARED mapping of /dev/zero.
  ASSERT_THAT(
      Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero.get(), 0),
      SyscallSucceeds());
}

TEST_F(MMapTest, MapDevZeroPrivate) {
  // This test will verify that we're able to map a page backed by /dev/zero
  // as MAP_PRIVATE.
  const FileDescriptor dev_zero =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR));

  // Test that we can create a RW SHARED mapping of /dev/zero.
  ASSERT_THAT(
      Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero.get(), 0),
      SyscallSucceeds());
}

TEST_F(MMapTest, MapDevZeroNoPersistence) {
  // This test will verify that two independent mappings of /dev/zero do not
  // appear to reference the same "backed file."

  const FileDescriptor dev_zero1 =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR));
  const FileDescriptor dev_zero2 =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR));

  ASSERT_THAT(
      Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero1.get(), 0),
      SyscallSucceeds());

  // Create a second mapping via the second /dev/zero fd.
  void* psec_map = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                        dev_zero2.get(), 0);
  ASSERT_THAT(reinterpret_cast<intptr_t>(psec_map), SyscallSucceeds());

  // Always unmap.
  auto cleanup_psec_map = Cleanup(
      [&] { EXPECT_THAT(munmap(psec_map, kPageSize), SyscallSucceeds()); });

  // Verify that we have independently addressed pages.
  ASSERT_NE(psec_map, addr_);

  std::string buf_zero(kPageSize, 0x00);
  std::string buf_ones(kPageSize, 0xFF);

  // Verify the first is actually all zeros after mmap.
  EXPECT_THAT(addr_, EqualsMemory(buf_zero));

  // Let's fill in the first mapping with 0xFF.
  memcpy(addr_, buf_ones.data(), kPageSize);

  // Verify that the memcpy actually stuck in the page.
  EXPECT_THAT(addr_, EqualsMemory(buf_ones));

  // Verify that it didn't affect the second page which should be all zeros.
  EXPECT_THAT(psec_map, EqualsMemory(buf_zero));
}

TEST_F(MMapTest, MapDevZeroSharedMultiplePages) {
  // This will test that we're able to map /dev/zero over multiple pages.
  const FileDescriptor dev_zero =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR));

  // Test that we can create a RW SHARED mapping of /dev/zero.
  ASSERT_THAT(Map(0, kPageSize * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE,
                  dev_zero.get(), 0),
              SyscallSucceeds());

  std::string buf_zero(kPageSize * 2, 0x00);
  std::string buf_ones(kPageSize * 2, 0xFF);

  // Verify the two pages are actually all zeros after mmap.
  EXPECT_THAT(addr_, EqualsMemory(buf_zero));

  // Fill out the pages with all ones.
  memcpy(addr_, buf_ones.data(), kPageSize * 2);

  // Verify that the memcpy actually stuck in the pages.
  EXPECT_THAT(addr_, EqualsMemory(buf_ones));
}

TEST_F(MMapTest, MapDevZeroSharedFdNoPersistence) {
  // This test will verify that two independent mappings of /dev/zero do not
  // appear to reference the same "backed file" even when mapped from the
  // same initial fd.
  const FileDescriptor dev_zero =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR));

  ASSERT_THAT(
      Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero.get(), 0),
      SyscallSucceeds());

  // Create a second mapping via the same fd.
  void* psec_map = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                        dev_zero.get(), 0);
  ASSERT_THAT(reinterpret_cast<int64_t>(psec_map), SyscallSucceeds());

  // Always unmap.
  auto cleanup_psec_map = Cleanup(
      [&] { ASSERT_THAT(munmap(psec_map, kPageSize), SyscallSucceeds()); });

  // Verify that we have independently addressed pages.
  ASSERT_NE(psec_map, addr_);

  std::string buf_zero(kPageSize, 0x00);
  std::string buf_ones(kPageSize, 0xFF);

  // Verify the first is actually all zeros after mmap.
  EXPECT_THAT(addr_, EqualsMemory(buf_zero));

  // Let's fill in the first mapping with 0xFF.
  memcpy(addr_, buf_ones.data(), kPageSize);

  // Verify that the memcpy actually stuck in the page.
  EXPECT_THAT(addr_, EqualsMemory(buf_ones));

  // Verify that it didn't affect the second page which should be all zeros.
  EXPECT_THAT(psec_map, EqualsMemory(buf_zero));
}

TEST_F(MMapTest, MapDevZeroSegfaultAfterUnmap) {
  SetupGvisorDeathTest();

  // This test will verify that we're able to map a page backed by /dev/zero
  // as MAP_SHARED and after it's unmapped any access results in a SIGSEGV.
  // This test is redundant but given the special nature of /dev/zero mappings
  // it doesn't hurt.
  const FileDescriptor dev_zero =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR));

  const auto rest = [&] {
    // Test that we can create a RW SHARED mapping of /dev/zero.
    TEST_PCHECK(Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                    dev_zero.get(),
                    0) != reinterpret_cast<uintptr_t>(MAP_FAILED));

    // Confirm that accesses after the unmap result in a SIGSEGV.
    //
    // N.B. We depend on this process being single-threaded to ensure there
    // can't be another mmap to map addr before the dereference below.
    void* addr_saved = addr_;  // Unmap resets addr_.
    TEST_PCHECK(Unmap() == 0);
    *reinterpret_cast<volatile int*>(addr_saved) = 0xFF;
  };

  int child_exit_status = ASSERT_NO_ERRNO_AND_VALUE(InForkedProcess(rest));
  EXPECT_TRUE(WIFSIGNALED(child_exit_status) &&
              WTERMSIG(child_exit_status) == SIGSEGV)
      << "exit status: " << child_exit_status;
}

TEST_F(MMapTest, MapDevZeroUnaligned) {
  const FileDescriptor dev_zero =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDWR));
  const size_t size = kPageSize + kPageSize / 2;
  const std::string buf_zero(size, 0x00);

  ASSERT_THAT(
      Map(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, dev_zero.get(), 0),
      SyscallSucceeds());
  EXPECT_THAT(addr_, EqualsMemory(buf_zero));
  ASSERT_THAT(Unmap(), SyscallSucceeds());

  ASSERT_THAT(
      Map(0, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, dev_zero.get(), 0),
      SyscallSucceeds());
  EXPECT_THAT(addr_, EqualsMemory(buf_zero));
}

// We can't map _some_ character devices.
TEST_F(MMapTest, MapCharDevice) {
  const FileDescriptor cdevfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/random", 0, 0));
  EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, cdevfd.get(), 0),
              SyscallFailsWithErrno(ENODEV));
}

// We can't map directories.
TEST_F(MMapTest, MapDirectory) {
  const FileDescriptor dirfd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(GetAbsoluteTestTmpdir(), 0, 0));
  EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_PRIVATE, dirfd.get(), 0),
              SyscallFailsWithErrno(ENODEV));
}

// We can map *something*
TEST_F(MMapTest, MapAnything) {
  EXPECT_THAT(Map(0, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallSucceedsWithValue(Gt(0)));
}

// Map length < PageSize allowed
TEST_F(MMapTest, SmallMap) {
  EXPECT_THAT(Map(0, 128, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallSucceeds());
}

// Hint address doesn't break anything.
// Note: there is no requirement we actually get the hint address
TEST_F(MMapTest, HintAddress) {
  EXPECT_THAT(
      Map(0x30000000, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
      SyscallSucceeds());
}

// MAP_FIXED gives us exactly the requested address
TEST_F(MMapTest, MapFixed) {
  EXPECT_THAT(Map(0x30000000, kPageSize, PROT_NONE,
                  MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0),
              SyscallSucceedsWithValue(0x30000000));
}

// 64-bit addresses work too
#if defined(__x86_64__) || defined(__aarch64__)
TEST_F(MMapTest, MapFixed64) {
  EXPECT_THAT(Map(0x300000000000, kPageSize, PROT_NONE,
                  MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0),
              SyscallSucceedsWithValue(0x300000000000));
}
#endif

// MAP_STACK allowed.
// There isn't a good way to verify it did anything.
TEST_F(MMapTest, MapStack) {
  EXPECT_THAT(Map(0, kPageSize, PROT_NONE,
                  MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0),
              SyscallSucceeds());
}

// MAP_LOCKED allowed.
// There isn't a good way to verify it did anything.
TEST_F(MMapTest, MapLocked) {
  EXPECT_THAT(Map(0, kPageSize, PROT_NONE,
                  MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED, -1, 0),
              SyscallSucceeds());
}

// MAP_PRIVATE or MAP_SHARED must be passed
TEST_F(MMapTest, NotPrivateOrShared) {
  EXPECT_THAT(Map(0, kPageSize, PROT_NONE, MAP_ANONYMOUS, -1, 0),
              SyscallFailsWithErrno(EINVAL));
}

// Only one of MAP_PRIVATE or MAP_SHARED may be passed
TEST_F(MMapTest, PrivateAndShared) {
  EXPECT_THAT(Map(0, kPageSize, PROT_NONE,
                  MAP_PRIVATE | MAP_SHARED | MAP_ANONYMOUS, -1, 0),
              SyscallFailsWithErrno(EINVAL));
}

TEST_F(MMapTest, FixedAlignment) {
  // Addr must be page aligned (MAP_FIXED)
  EXPECT_THAT(Map(0x30000001, kPageSize, PROT_NONE,
                  MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0),
              SyscallFailsWithErrno(EINVAL));
}

// Non-MAP_FIXED address does not need to be page aligned
TEST_F(MMapTest, NonFixedAlignment) {
  EXPECT_THAT(
      Map(0x30000001, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
      SyscallSucceeds());
}

// Length = 0 results in EINVAL.
TEST_F(MMapTest, InvalidLength) {
  EXPECT_THAT(Map(0, 0, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallFailsWithErrno(EINVAL));
}

// Bad fd not allowed.
TEST_F(MMapTest, BadFd) {
  EXPECT_THAT(Map(0, kPageSize, PROT_NONE, MAP_PRIVATE, 999, 0),
              SyscallFailsWithErrno(EBADF));
}

// Mappings are writable.
TEST_F(MMapTest, ProtWrite) {
  uint64_t addr;
  constexpr uint8_t kFirstWord[] = {42, 42, 42, 42};

  EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallSucceeds());

  // This shouldn't cause a SIGSEGV.
  memset(reinterpret_cast<void*>(addr), 42, kPageSize);

  // The written data should actually be there.
  EXPECT_EQ(
      0, memcmp(reinterpret_cast<void*>(addr), kFirstWord, sizeof(kFirstWord)));
}

// "Write-only" mappings are writable *and* readable.
TEST_F(MMapTest, ProtWriteOnly) {
  uint64_t addr;
  constexpr uint8_t kFirstWord[] = {42, 42, 42, 42};

  EXPECT_THAT(
      addr = Map(0, kPageSize, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
      SyscallSucceeds());

  // This shouldn't cause a SIGSEGV.
  memset(reinterpret_cast<void*>(addr), 42, kPageSize);

  // The written data should actually be there.
  EXPECT_EQ(
      0, memcmp(reinterpret_cast<void*>(addr), kFirstWord, sizeof(kFirstWord)));
}

// "Write-only" mappings are readable.
//
// This is distinct from above to ensure the page is accessible even if the
// initial fault is a write fault.
TEST_F(MMapTest, ProtWriteOnlyReadable) {
  uint64_t addr;
  constexpr uint64_t kFirstWord = 0;

  EXPECT_THAT(
      addr = Map(0, kPageSize, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
      SyscallSucceeds());

  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), &kFirstWord,
                      sizeof(kFirstWord)));
}

// Mappings are writable after mprotect from PROT_NONE to PROT_READ|PROT_WRITE.
TEST_F(MMapTest, ProtectProtWrite) {
  uint64_t addr;
  constexpr uint8_t kFirstWord[] = {42, 42, 42, 42};

  EXPECT_THAT(
      addr = Map(0, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
      SyscallSucceeds());

  ASSERT_THAT(Protect(addr, kPageSize, PROT_READ | PROT_WRITE),
              SyscallSucceeds());

  // This shouldn't cause a SIGSEGV.
  memset(reinterpret_cast<void*>(addr), 42, kPageSize);

  // The written data should actually be there.
  EXPECT_EQ(
      0, memcmp(reinterpret_cast<void*>(addr), kFirstWord, sizeof(kFirstWord)));
}

// SIGSEGV raised when reading PROT_NONE memory
TEST_F(MMapTest, ProtNoneDeath) {
  SetupGvisorDeathTest();

  uintptr_t addr;

  ASSERT_THAT(
      addr = Map(0, kPageSize, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
      SyscallSucceeds());

  EXPECT_EXIT(*reinterpret_cast<volatile int*>(addr),
              ::testing::KilledBySignal(SIGSEGV), "");
}

// SIGSEGV raised when writing PROT_READ only memory
TEST_F(MMapTest, ReadOnlyDeath) {
  SetupGvisorDeathTest();

  uintptr_t addr;

  ASSERT_THAT(
      addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
      SyscallSucceeds());

  EXPECT_EXIT(*reinterpret_cast<volatile int*>(addr) = 42,
              ::testing::KilledBySignal(SIGSEGV), "");
}

// Writable mapping mprotect'd to read-only should not be writable.
TEST_F(MMapTest, MprotectReadOnlyDeath) {
  SetupGvisorDeathTest();

  uintptr_t addr;

  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallSucceeds());

  volatile int* val = reinterpret_cast<int*>(addr);

  // Copy to ensure page is mapped in.
  *val = 42;

  ASSERT_THAT(Protect(addr, kPageSize, PROT_READ), SyscallSucceeds());

  // Now it shouldn't be writable.
  EXPECT_EXIT(*val = 0, ::testing::KilledBySignal(SIGSEGV), "");
}

// Verify that calling mprotect an address that's not page aligned fails.
TEST_F(MMapTest, MprotectNotPageAligned) {
  uintptr_t addr;

  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallSucceeds());
  ASSERT_THAT(Protect(addr + 1, kPageSize - 1, PROT_READ),
              SyscallFailsWithErrno(EINVAL));
}

// Verify that regions mprotect'ed to PROT_NONE return SEGV_ACCERR.
TEST_F(MMapTest, MprotectProtNone) {
  const auto rest = [&] {
    void* p = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    TEST_PCHECK(p != MAP_FAILED);
    *static_cast<volatile char*>(p) = 1;  // ensure page is present
    TEST_PCHECK(mprotect(p, kPageSize, PROT_NONE) == 0);

    struct sigaction sa = {};
    sa.sa_sigaction = MprotectProtNoneHandler;
    sa.sa_flags = SA_SIGINFO;
    TEST_PCHECK(sigaction(SIGSEGV, &sa, nullptr) == 0);

    if (sigsetjmp(jmpbuf, 1) == 0) {
      (void)*static_cast<volatile char*>(p);  // trigger fault
    }

    TEST_CHECK(si_code_received == SEGV_ACCERR);
  };
  EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}

// Verify that calling mprotect with an absurdly huge length fails.
TEST_F(MMapTest, MprotectHugeLength) {
  uintptr_t addr;

  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallSucceeds());
  ASSERT_THAT(Protect(addr, static_cast<size_t>(-1), PROT_READ),
              SyscallFailsWithErrno(ENOMEM));
}

#if defined(__x86_64__) || defined(__i386__)
// This code is equivalent in 32 and 64-bit mode
const uint8_t machine_code[] = {
    0xb8, 0x2a, 0x00, 0x00, 0x00,  // movl $42, %eax
    0xc3,                          // retq
};
#elif defined(__aarch64__)
const uint8_t machine_code[] = {
    0x40, 0x05, 0x80, 0x52,  // mov w0, #42
    0xc0, 0x03, 0x5f, 0xd6,  // ret
};
#elif defined(__riscv)
const uint8_t machine_code[] = {
    0x13, 0x05, 0xa0, 0x02,  // li a0,42
    0x82, 0x80               // ret
};
#endif

// PROT_EXEC allows code execution
TEST_F(MMapTest, ProtExec) {
  uintptr_t addr;
  uint32_t (*func)(void);

  EXPECT_THAT(addr = Map(0, kPageSize, PROT_EXEC | PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallSucceeds());

  memcpy(reinterpret_cast<void*>(addr), machine_code, sizeof(machine_code));

#if defined(__aarch64__)
  // We use this as a memory barrier for Arm64.
  ASSERT_THAT(Protect(addr, kPageSize, PROT_READ | PROT_EXEC),
              SyscallSucceeds());
#endif

  func = reinterpret_cast<uint32_t (*)(void)>(addr);

  EXPECT_EQ(42, func());
}

// No PROT_EXEC disallows code execution
TEST_F(MMapTest, NoProtExecDeath) {
  SetupGvisorDeathTest();

  uintptr_t addr;
  uint32_t (*func)(void);

  EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallSucceeds());

  memcpy(reinterpret_cast<void*>(addr), machine_code, sizeof(machine_code));

  func = reinterpret_cast<uint32_t (*)(void)>(addr);

  EXPECT_EXIT(func(), ::testing::KilledBySignal(SIGSEGV), "");
}

TEST_F(MMapTest, NoExceedLimitData) {
  void* prevbrk;
  void* target_brk;
  struct rlimit setlim;

  prevbrk = sbrk(0);
  ASSERT_NE(-1, reinterpret_cast<intptr_t>(prevbrk));
  target_brk = reinterpret_cast<char*>(prevbrk) + 1;

  setlim.rlim_cur = RLIM_INFINITY;
  setlim.rlim_max = RLIM_INFINITY;
  ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds());
  EXPECT_THAT(brk(target_brk), SyscallSucceedsWithValue(0));
}

TEST_F(MMapTest, ExceedLimitData) {
  // To unit test this more precisely, we'd need access to the mm's start_brk
  // and end_brk, which we don't have direct access to :/
  void* prevbrk;
  void* target_brk;
  struct rlimit setlim;

  prevbrk = sbrk(0);
  ASSERT_NE(-1, reinterpret_cast<intptr_t>(prevbrk));
  target_brk = reinterpret_cast<char*>(prevbrk) + 8192;

  setlim.rlim_cur = 0;
  setlim.rlim_max = RLIM_INFINITY;
  // Set RLIMIT_DATA very low so any subsequent brk() calls fail.
  // Reset RLIMIT_DATA during teardown step.
  ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds());
  EXPECT_THAT(brk(target_brk), SyscallFailsWithErrno(ENOMEM));
  // Teardown step...
  setlim.rlim_cur = RLIM_INFINITY;
  ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds());
}

TEST_F(MMapTest, ExceedLimitDataPrlimit) {
  // To unit test this more precisely, we'd need access to the mm's start_brk
  // and end_brk, which we don't have direct access to :/
  void* prevbrk;
  void* target_brk;
  struct rlimit setlim;

  prevbrk = sbrk(0);
  ASSERT_NE(-1, reinterpret_cast<intptr_t>(prevbrk));
  target_brk = reinterpret_cast<char*>(prevbrk) + 8192;

  setlim.rlim_cur = 0;
  setlim.rlim_max = RLIM_INFINITY;
  // Set RLIMIT_DATA very low so any subsequent brk() calls fail.
  // Reset RLIMIT_DATA during teardown step.
  ASSERT_THAT(prlimit(0, RLIMIT_DATA, &setlim, nullptr), SyscallSucceeds());
  EXPECT_THAT(brk(target_brk), SyscallFailsWithErrno(ENOMEM));
  // Teardown step...
  setlim.rlim_cur = RLIM_INFINITY;
  ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds());
}

TEST_F(MMapTest, ExceedLimitDataPrlimitPID) {
  // To unit test this more precisely, we'd need access to the mm's start_brk
  // and end_brk, which we don't have direct access to :/
  void* prevbrk;
  void* target_brk;
  struct rlimit setlim;

  prevbrk = sbrk(0);
  ASSERT_NE(-1, reinterpret_cast<intptr_t>(prevbrk));
  target_brk = reinterpret_cast<char*>(prevbrk) + 8192;

  setlim.rlim_cur = 0;
  setlim.rlim_max = RLIM_INFINITY;
  // Set RLIMIT_DATA very low so any subsequent brk() calls fail.
  // Reset RLIMIT_DATA during teardown step.
  ASSERT_THAT(prlimit(syscall(__NR_gettid), RLIMIT_DATA, &setlim, nullptr),
              SyscallSucceeds());
  EXPECT_THAT(brk(target_brk), SyscallFailsWithErrno(ENOMEM));
  // Teardown step...
  setlim.rlim_cur = RLIM_INFINITY;
  ASSERT_THAT(setrlimit(RLIMIT_DATA, &setlim), SyscallSucceeds());
}

TEST_F(MMapTest, NoExceedLimitAS) {
  constexpr uint64_t kAllocBytes = 200 << 20;
  // Add some headroom to the AS limit in case of e.g. unexpected stack
  // expansion.
  constexpr uint64_t kExtraASBytes = kAllocBytes + (20 << 20);
  static_assert(kAllocBytes < kExtraASBytes,
                "test depends on allocation not exceeding AS limit");

  auto vss = ASSERT_NO_ERRNO_AND_VALUE(VirtualMemorySize());
  struct rlimit setlim;
  setlim.rlim_cur = vss + kExtraASBytes;
  setlim.rlim_max = RLIM_INFINITY;
  ASSERT_THAT(setrlimit(RLIMIT_AS, &setlim), SyscallSucceeds());
  EXPECT_THAT(
      Map(0, kAllocBytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
      SyscallSucceedsWithValue(Gt(0)));
}

TEST_F(MMapTest, ExceedLimitAS) {
  constexpr uint64_t kAllocBytes = 200 << 20;
  // Add some headroom to the AS limit in case of e.g. unexpected stack
  // expansion.
  constexpr uint64_t kExtraASBytes = 20 << 20;
  static_assert(kAllocBytes > kExtraASBytes,
                "test depends on allocation exceeding AS limit");

  auto vss = ASSERT_NO_ERRNO_AND_VALUE(VirtualMemorySize());
  struct rlimit setlim;
  setlim.rlim_cur = vss + kExtraASBytes;
  setlim.rlim_max = RLIM_INFINITY;
  ASSERT_THAT(setrlimit(RLIMIT_AS, &setlim), SyscallSucceeds());
  EXPECT_THAT(
      Map(0, kAllocBytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
      SyscallFailsWithErrno(ENOMEM));
}

// Tests that setting an anonymous mmap to PROT_NONE doesn't free the memory.
TEST_F(MMapTest, SettingProtNoneDoesntFreeMemory) {
  uintptr_t addr;
  constexpr uint8_t kFirstWord[] = {42, 42, 42, 42};

  EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallSucceedsWithValue(Gt(0)));

  memset(reinterpret_cast<void*>(addr), 42, kPageSize);

  ASSERT_THAT(Protect(addr, kPageSize, PROT_NONE), SyscallSucceeds());
  ASSERT_THAT(Protect(addr, kPageSize, PROT_READ | PROT_WRITE),
              SyscallSucceeds());

  // The written data should still be there.
  EXPECT_EQ(
      0, memcmp(reinterpret_cast<void*>(addr), kFirstWord, sizeof(kFirstWord)));
}

constexpr char kFileContents[] = "Hello World!";

class MMapFileTest : public MMapTest {
 protected:
  FileDescriptor fd_;
  std::string filename_;

  // Open a file for read/write
  void SetUp() override {
    MMapTest::SetUp();

    filename_ = NewTempAbsPath();
    fd_ = ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_CREAT | O_RDWR, 0644));

    // Extend file so it can be written once mapped. Deliberately make the file
    // only half a page in size, so we can test what happens when we access the
    // second half.
    // Use ftruncate(2) once the sentry supports it.
    char zero = 0;
    size_t count = 0;
    do {
      const DisableSave ds;  // saving 2048 times is slow and useless.
      Write(&zero, 1), SyscallSucceedsWithValue(1);
    } while (++count < (kPageSize / 2));
    ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0));
  }

  // Close and delete file
  void TearDown() override {
    MMapTest::TearDown();
    fd_.reset();  // Make sure the files is closed before we unlink it.
    ASSERT_THAT(unlink(filename_.c_str()), SyscallSucceeds());
  }

  bool FSSupportsMap() const {
    bool supported = true;
    void* ret = mmap(nullptr, 1, PROT_NONE, MAP_PRIVATE, fd_.get(), 0);
    if (ret == MAP_FAILED && errno != ENODEV) {
      supported = false;
    }
    if (ret != MAP_FAILED) {
      munmap(ret, 1);
    }

    return supported;
  }

  ssize_t Read(char* buf, size_t count) {
    ssize_t len = 0;
    do {
      ssize_t ret = read(fd_.get(), buf, count);
      if (ret < 0) {
        return ret;
      } else if (ret == 0) {
        return len;
      }

      len += ret;
      buf += ret;
    } while (len < static_cast<ssize_t>(count));

    return len;
  }

  ssize_t Write(const char* buf, size_t count) {
    ssize_t len = 0;
    do {
      ssize_t ret = write(fd_.get(), buf, count);
      if (ret < 0) {
        return ret;
      } else if (ret == 0) {
        return len;
      }

      len += ret;
      buf += ret;
    } while (len < static_cast<ssize_t>(count));

    return len;
  }
};

class MMapFileParamTest
    : public MMapFileTest,
      public ::testing::WithParamInterface<std::tuple<int, int>> {
 protected:
  int prot() const { return std::get<0>(GetParam()); }

  int flags() const { return std::get<1>(GetParam()); }
};

// MAP_POPULATE allowed.
// There isn't a good way to verify it actually did anything.
TEST_P(MMapFileParamTest, MapPopulate) {
  SKIP_IF(!FSSupportsMap());
  ASSERT_THAT(Map(0, kPageSize, prot(), flags() | MAP_POPULATE, fd_.get(), 0),
              SyscallSucceeds());
}

// MAP_POPULATE on a short file.
TEST_P(MMapFileParamTest, MapPopulateShort) {
  SKIP_IF(!FSSupportsMap());
  ASSERT_THAT(
      Map(0, 2 * kPageSize, prot(), flags() | MAP_POPULATE, fd_.get(), 0),
      SyscallSucceeds());
}

// Read contents from mapped file.
TEST_F(MMapFileTest, Read) {
  SKIP_IF(!FSSupportsMap());
  size_t len = strlen(kFileContents);
  ASSERT_EQ(len, Write(kFileContents, len));

  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fd_.get(), 0),
              SyscallSucceeds());

  EXPECT_THAT(reinterpret_cast<char*>(addr),
              EqualsMemory(std::string(kFileContents)));
}

// Map at an offset.
TEST_F(MMapFileTest, MapOffset) {
  SKIP_IF(!FSSupportsMap());
  ASSERT_THAT(lseek(fd_.get(), kPageSize, SEEK_SET), SyscallSucceeds());

  size_t len = strlen(kFileContents);
  ASSERT_EQ(len, Write(kFileContents, len));

  uintptr_t addr;
  ASSERT_THAT(
      addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fd_.get(), kPageSize),
      SyscallSucceeds());

  EXPECT_THAT(reinterpret_cast<char*>(addr),
              EqualsMemory(std::string(kFileContents)));
}

TEST_F(MMapFileTest, MapOffsetBeyondEnd) {
  SKIP_IF(!FSSupportsMap());
  SetupGvisorDeathTest();

  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE,
                         fd_.get(), 10 * kPageSize),
              SyscallSucceeds());

  // Touching the memory causes SIGBUS.
  size_t len = strlen(kFileContents);
  EXPECT_EXIT(std::copy(kFileContents, kFileContents + len,
                        reinterpret_cast<volatile char*>(addr)),
              ::testing::KilledBySignal(SIGBUS), "");
}

TEST_F(MMapFileTest, MapSecondToLastPositivePage) {
  SKIP_IF(!FSSupportsMap());
  EXPECT_THAT(
      Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(),
          (std::numeric_limits<off_t>::max() - kPageSize) & ~(kPageSize - 1)),
      SyscallSucceeds());
}

TEST_F(MMapFileTest, MapLastPositivePage) {
  SKIP_IF(!FSSupportsMap());
  // For regular files, this should fail due to integer overflow of the end
  // offset.
  EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(),
                  std::numeric_limits<off_t>::max() & ~(kPageSize - 1)),
              SyscallFailsWithErrno(EOVERFLOW));
}

TEST_F(MMapFileTest, MapFirstNegativePage) {
  SKIP_IF(!FSSupportsMap());
  EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(),
                  std::numeric_limits<off_t>::min()),
              SyscallFailsWithErrno(EOVERFLOW));
}

TEST_F(MMapFileTest, MapSecondToLastNegativePage) {
  SKIP_IF(!FSSupportsMap());
  EXPECT_THAT(
      Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), -(2 * kPageSize)),
      SyscallFailsWithErrno(EOVERFLOW));
}

TEST_F(MMapFileTest, MapLastNegativePage) {
  SKIP_IF(!FSSupportsMap());
  EXPECT_THAT(Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), -kPageSize),
              SyscallFailsWithErrno(EOVERFLOW));
}

// MAP_PRIVATE PROT_WRITE is allowed on read-only FDs.
TEST_F(MMapFileTest, WritePrivateOnReadOnlyFd) {
  SKIP_IF(!FSSupportsMap());
  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_RDONLY));

  uintptr_t addr;
  EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE,
                         fd.get(), 0),
              SyscallSucceeds());

  // Touch the page to ensure the kernel didn't lie about writability.
  size_t len = strlen(kFileContents);
  std::copy(kFileContents, kFileContents + len,
            reinterpret_cast<volatile char*>(addr));
}

// MAP_SHARED PROT_WRITE not allowed on read-only FDs.
TEST_F(MMapFileTest, WriteSharedOnReadOnlyFd) {
  SKIP_IF(!FSSupportsMap());
  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_RDONLY));

  uintptr_t addr;
  EXPECT_THAT(
      addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0),
      SyscallFailsWithErrno(EACCES));
}

// Mmap not allowed on O_PATH FDs.
TEST_F(MMapFileTest, MmapFileWithOpath) {
  SKIP_IF(!FSSupportsMap());
  const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH));

  uintptr_t addr;
  EXPECT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_PRIVATE, fd.get(), 0),
              SyscallFailsWithErrno(EBADF));
}

// The FD must be readable.
TEST_P(MMapFileParamTest, WriteOnlyFd) {
  SKIP_IF(!FSSupportsMap());
  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(filename_, O_WRONLY));

  uintptr_t addr;
  EXPECT_THAT(addr = Map(0, kPageSize, prot(), flags(), fd.get(), 0),
              SyscallFailsWithErrno(EACCES));
}

// Overwriting the contents of a file mapped MAP_SHARED PROT_READ
// should cause the new data to be reflected in the mapping.
TEST_F(MMapFileTest, ReadSharedConsistentWithOverwrite) {
  SKIP_IF(!FSSupportsMap());
  // Start from scratch.
  EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());

  // Expand the file to two pages and dirty them.
  std::string bufA(kPageSize, 'a');
  ASSERT_THAT(Write(bufA.c_str(), bufA.size()),
              SyscallSucceedsWithValue(bufA.size()));
  std::string bufB(kPageSize, 'b');
  ASSERT_THAT(Write(bufB.c_str(), bufB.size()),
              SyscallSucceedsWithValue(bufB.size()));

  // Map the page.
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0),
              SyscallSucceeds());

  // Check that the mapping contains the right file data.
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), bufA.c_str(), kPageSize));
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr + kPageSize), bufB.c_str(),
                      kPageSize));

  // Start at the beginning of the file.
  ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0));

  // Swap the write pattern.
  ASSERT_THAT(Write(bufB.c_str(), bufB.size()),
              SyscallSucceedsWithValue(bufB.size()));
  ASSERT_THAT(Write(bufA.c_str(), bufA.size()),
              SyscallSucceedsWithValue(bufA.size()));

  // Check that the mapping got updated.
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), bufB.c_str(), kPageSize));
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr + kPageSize), bufA.c_str(),
                      kPageSize));
}

// Partially overwriting a file mapped MAP_SHARED PROT_READ should be reflected
// in the mapping.
TEST_F(MMapFileTest, ReadSharedConsistentWithPartialOverwrite) {
  SKIP_IF(!FSSupportsMap());
  // Start from scratch.
  EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());

  // Expand the file to two pages and dirty them.
  std::string bufA(kPageSize, 'a');
  ASSERT_THAT(Write(bufA.c_str(), bufA.size()),
              SyscallSucceedsWithValue(bufA.size()));
  std::string bufB(kPageSize, 'b');
  ASSERT_THAT(Write(bufB.c_str(), bufB.size()),
              SyscallSucceedsWithValue(bufB.size()));

  // Map the page.
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0),
              SyscallSucceeds());

  // Check that the mapping contains the right file data.
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), bufA.c_str(), kPageSize));
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr + kPageSize), bufB.c_str(),
                      kPageSize));

  // Start at the beginning of the file.
  ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0));

  // Do a partial overwrite, spanning both pages.
  std::string bufC(kPageSize + (kPageSize / 2), 'c');
  ASSERT_THAT(Write(bufC.c_str(), bufC.size()),
              SyscallSucceedsWithValue(bufC.size()));

  // Check that the mapping got updated.
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), bufC.c_str(),
                      kPageSize + (kPageSize / 2)));
  EXPECT_EQ(0,
            memcmp(reinterpret_cast<void*>(addr + kPageSize + (kPageSize / 2)),
                   bufB.c_str(), kPageSize / 2));
}

// Overwriting a file mapped MAP_SHARED PROT_READ should be reflected in the
// mapping and the file.
TEST_F(MMapFileTest, ReadSharedConsistentWithWriteAndFile) {
  SKIP_IF(!FSSupportsMap());
  // Start from scratch.
  EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());

  // Expand the file to two full pages and dirty it.
  std::string bufA(2 * kPageSize, 'a');
  ASSERT_THAT(Write(bufA.c_str(), bufA.size()),
              SyscallSucceedsWithValue(bufA.size()));

  // Map only the first page.
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0),
              SyscallSucceeds());

  // Prepare to overwrite the file contents.
  ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0));

  // Overwrite everything, beyond the mapped portion.
  std::string bufB(2 * kPageSize, 'b');
  ASSERT_THAT(Write(bufB.c_str(), bufB.size()),
              SyscallSucceedsWithValue(bufB.size()));

  // What the mapped portion should now look like.
  std::string bufMapped(kPageSize, 'b');

  // Expect that the mapped portion is consistent.
  EXPECT_EQ(
      0, memcmp(reinterpret_cast<void*>(addr), bufMapped.c_str(), kPageSize));

  // Prepare to read the entire file contents.
  ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0));

  // Expect that the file was fully updated.
  std::vector<char> bufFile(2 * kPageSize);
  ASSERT_THAT(Read(bufFile.data(), bufFile.size()),
              SyscallSucceedsWithValue(bufFile.size()));
  // Cast to void* to avoid EXPECT_THAT assuming bufFile.data() is a
  // NUL-terminated C std::string. EXPECT_THAT will try to print a char* as a C
  // std::string, possibly overruning the buffer.
  EXPECT_THAT(reinterpret_cast<void*>(bufFile.data()), EqualsMemory(bufB));
}

// Write data to mapped file.
TEST_F(MMapFileTest, WriteShared) {
  SKIP_IF(!FSSupportsMap());
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd_.get(), 0),
              SyscallSucceeds());

  size_t len = strlen(kFileContents);
  memcpy(reinterpret_cast<void*>(addr), kFileContents, len);

  // The file may not actually be updated until munmap is called.
  ASSERT_THAT(Unmap(), SyscallSucceeds());

  std::vector<char> buf(len);
  ASSERT_THAT(Read(buf.data(), buf.size()),
              SyscallSucceedsWithValue(buf.size()));
  // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a
  // NUL-terminated C string. EXPECT_THAT will try to print a char* as a C
  // string, possibly overruning the buffer.
  EXPECT_THAT(reinterpret_cast<void*>(buf.data()),
              EqualsMemory(std::string(kFileContents)));
}

// Write data to portion of mapped page beyond the end of the file.
// These writes are not reflected in the file.
TEST_F(MMapFileTest, WriteSharedBeyondEnd) {
  SKIP_IF(!FSSupportsMap());
  // The file is only half of a page. We map an entire page. Writes to the
  // end of the mapping must not be reflected in the file.
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd_.get(), 0),
              SyscallSucceeds());

  // First half; this is reflected in the file.
  std::string first(kPageSize / 2, 'A');
  memcpy(reinterpret_cast<void*>(addr), first.c_str(), first.size());

  // Second half; this is not reflected in the file.
  std::string second(kPageSize / 2, 'B');
  memcpy(reinterpret_cast<void*>(addr + kPageSize / 2), second.c_str(),
         second.size());

  // The file may not actually be updated until munmap is called.
  ASSERT_THAT(Unmap(), SyscallSucceeds());

  // Big enough to fit the entire page, if the writes are mistakenly written to
  // the file.
  std::vector<char> buf(kPageSize);

  // Only the first half is in the file.
  ASSERT_THAT(Read(buf.data(), buf.size()),
              SyscallSucceedsWithValue(first.size()));
  // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a
  // NUL-terminated C string. EXPECT_THAT will try to print a char* as a C
  // NUL-terminated C std::string. EXPECT_THAT will try to print a char* as a C
  // std::string, possibly overruning the buffer.
  EXPECT_THAT(reinterpret_cast<void*>(buf.data()), EqualsMemory(first));
}

// The portion of a mapped page that becomes part of the file after a truncate
// is reflected in the file.
TEST_F(MMapFileTest, WriteSharedTruncateUp) {
  SKIP_IF(!FSSupportsMap());
  // The file is only half of a page. We map an entire page. Writes to the
  // end of the mapping must not be reflected in the file.
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd_.get(), 0),
              SyscallSucceeds());

  // First half; this is reflected in the file.
  std::string first(kPageSize / 2, 'A');
  memcpy(reinterpret_cast<void*>(addr), first.c_str(), first.size());

  // Extend the file to a full page.
  EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds());

  // Second half; this will be reflected in the file now.
  std::string second(kPageSize / 2, 'B');
  memcpy(reinterpret_cast<void*>(addr + kPageSize / 2), second.c_str(),
         second.size());

  // The file may not actually be updated until munmap is called.
  ASSERT_THAT(Unmap(), SyscallSucceeds());

  // The whole page is in the file.
  std::vector<char> buf(kPageSize);
  ASSERT_THAT(Read(buf.data(), buf.size()),
              SyscallSucceedsWithValue(buf.size()));
  // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a
  // NUL-terminated C string. EXPECT_THAT will try to print a char* as a C
  // string, possibly overruning the buffer.
  EXPECT_THAT(reinterpret_cast<void*>(buf.data()), EqualsMemory(first));
  EXPECT_THAT(reinterpret_cast<void*>(buf.data() + kPageSize / 2),
              EqualsMemory(second));
}

TEST_F(MMapFileTest, ReadSharedTruncateDownThenUp) {
  SKIP_IF(!FSSupportsMap());
  // Start from scratch.
  EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());

  // Expand the file to a full page and dirty it.
  std::string buf(kPageSize, 'a');
  ASSERT_THAT(Write(buf.c_str(), buf.size()),
              SyscallSucceedsWithValue(buf.size()));

  // Map the page.
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0),
              SyscallSucceeds());

  // Check that the memory contains the file data.
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), buf.c_str(), kPageSize));

  // Truncate down, then up.
  EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());
  EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds());

  // Check that the memory was zeroed.
  std::string zeroed(kPageSize, '\0');
  EXPECT_EQ(0,
            memcmp(reinterpret_cast<void*>(addr), zeroed.c_str(), kPageSize));

  // The file may not actually be updated until msync is called.
  ASSERT_THAT(Msync(), SyscallSucceeds());

  // Prepare to read the entire file contents.
  ASSERT_THAT(lseek(fd_.get(), 0, SEEK_SET), SyscallSucceedsWithValue(0));

  // Expect that the file is fully updated.
  std::vector<char> bufFile(kPageSize);
  ASSERT_THAT(Read(bufFile.data(), bufFile.size()),
              SyscallSucceedsWithValue(bufFile.size()));
  EXPECT_EQ(0, memcmp(bufFile.data(), zeroed.c_str(), kPageSize));
}

TEST_F(MMapFileTest, WriteSharedTruncateDownThenUp) {
  SKIP_IF(!FSSupportsMap());
  // The file is only half of a page. We map an entire page. Writes to the
  // end of the mapping must not be reflected in the file.
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd_.get(), 0),
              SyscallSucceeds());

  // First half; this will be deleted by truncate(0).
  std::string first(kPageSize / 2, 'A');
  memcpy(reinterpret_cast<void*>(addr), first.c_str(), first.size());

  // Truncate down, then up.
  EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());
  EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds());

  // The whole page is zeroed in memory.
  std::string zeroed(kPageSize, '\0');
  EXPECT_EQ(0,
            memcmp(reinterpret_cast<void*>(addr), zeroed.c_str(), kPageSize));

  // The file may not actually be updated until munmap is called.
  ASSERT_THAT(Unmap(), SyscallSucceeds());

  // The whole file is also zeroed.
  std::vector<char> buf(kPageSize);
  ASSERT_THAT(Read(buf.data(), buf.size()),
              SyscallSucceedsWithValue(buf.size()));
  // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a
  // NUL-terminated C string. EXPECT_THAT will try to print a char* as a C
  // string, possibly overruning the buffer.
  EXPECT_THAT(reinterpret_cast<void*>(buf.data()), EqualsMemory(zeroed));
}

TEST_F(MMapFileTest, ReadSharedTruncateSIGBUS) {
  SKIP_IF(!FSSupportsMap());
  SetupGvisorDeathTest();

  // Start from scratch.
  EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());

  // Expand the file to a full page and dirty it.
  std::string buf(kPageSize, 'a');
  ASSERT_THAT(Write(buf.c_str(), buf.size()),
              SyscallSucceedsWithValue(buf.size()));

  // Map the page.
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0),
              SyscallSucceeds());

  // Check that the mapping contains the file data.
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), buf.c_str(), kPageSize));

  // Truncate down.
  EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());

  // Accessing the truncated region should cause a SIGBUS.
  std::vector<char> in(kPageSize);
  EXPECT_EXIT(
      std::copy(reinterpret_cast<volatile char*>(addr),
                reinterpret_cast<volatile char*>(addr) + kPageSize, in.data()),
      ::testing::KilledBySignal(SIGBUS), "");
}

TEST_F(MMapFileTest, WriteSharedTruncateSIGBUS) {
  SKIP_IF(!FSSupportsMap());
  SetupGvisorDeathTest();

  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd_.get(), 0),
              SyscallSucceeds());

  // Touch the memory to be sure it really is mapped.
  size_t len = strlen(kFileContents);
  memcpy(reinterpret_cast<void*>(addr), kFileContents, len);

  // Truncate down.
  EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());

  // Accessing the truncated file should cause a SIGBUS.
  EXPECT_EXIT(std::copy(kFileContents, kFileContents + len,
                        reinterpret_cast<volatile char*>(addr)),
              ::testing::KilledBySignal(SIGBUS), "");
}

TEST_F(MMapFileTest, ReadSharedTruncatePartialPage) {
  SKIP_IF(!FSSupportsMap());
  // Start from scratch.
  EXPECT_THAT(ftruncate(fd_.get(), 0), SyscallSucceeds());

  // Dirty the file.
  std::string buf(kPageSize, 'a');
  ASSERT_THAT(Write(buf.c_str(), buf.size()),
              SyscallSucceedsWithValue(buf.size()));

  // Map a page.
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0),
              SyscallSucceeds());

  // Truncate to half of the page.
  EXPECT_THAT(ftruncate(fd_.get(), kPageSize / 2), SyscallSucceeds());

  // First half of the page untouched.
  EXPECT_EQ(0,
            memcmp(reinterpret_cast<void*>(addr), buf.data(), kPageSize / 2));

  // Second half is zeroed.
  std::string zeroed(kPageSize / 2, '\0');
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr + kPageSize / 2),
                      zeroed.c_str(), kPageSize / 2));
}

// Page can still be accessed and contents are intact after truncating a partial
// page.
TEST_F(MMapFileTest, WriteSharedTruncatePartialPage) {
  SKIP_IF(!FSSupportsMap());
  // Expand the file to a full page.
  EXPECT_THAT(ftruncate(fd_.get(), kPageSize), SyscallSucceeds());

  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd_.get(), 0),
              SyscallSucceeds());

  // Fill the entire page.
  std::string contents(kPageSize, 'A');
  memcpy(reinterpret_cast<void*>(addr), contents.c_str(), contents.size());

  // Truncate half of the page.
  EXPECT_THAT(ftruncate(fd_.get(), kPageSize / 2), SyscallSucceeds());

  // First half of the page untouched.
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr), contents.c_str(),
                      kPageSize / 2));

  // Second half zeroed.
  std::string zeroed(kPageSize / 2, '\0');
  EXPECT_EQ(0, memcmp(reinterpret_cast<void*>(addr + kPageSize / 2),
                      zeroed.c_str(), kPageSize / 2));
}

// MAP_PRIVATE writes are not carried through to the underlying file.
TEST_F(MMapFileTest, WritePrivate) {
  SKIP_IF(!FSSupportsMap());
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE,
                         fd_.get(), 0),
              SyscallSucceeds());

  size_t len = strlen(kFileContents);
  memcpy(reinterpret_cast<void*>(addr), kFileContents, len);

  // The file should not be updated, but if it mistakenly is, it may not be
  // until after munmap is called.
  ASSERT_THAT(Unmap(), SyscallSucceeds());

  std::vector<char> buf(len);
  ASSERT_THAT(Read(buf.data(), buf.size()),
              SyscallSucceedsWithValue(buf.size()));
  // Cast to void* to avoid EXPECT_THAT assuming buf.data() is a
  // NUL-terminated C string. EXPECT_THAT will try to print a char* as a C
  // string, possibly overruning the buffer.
  EXPECT_THAT(reinterpret_cast<void*>(buf.data()),
              EqualsMemory(std::string(len, '\0')));
}

// SIGBUS raised when reading or writing past end of a mapped file.
TEST_P(MMapFileParamTest, SigBusDeath) {
  SKIP_IF(!FSSupportsMap());
  SetupGvisorDeathTest();

  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, 2 * kPageSize, prot(), flags(), fd_.get(), 0),
              SyscallSucceeds());

  auto* start = reinterpret_cast<volatile char*>(addr + kPageSize);

  // MMapFileTest makes a file kPageSize/2 long. The entire first page should be
  // accessible, but anything beyond it should not.
  if (prot() & PROT_WRITE) {
    // Write beyond first page.
    size_t len = strlen(kFileContents);
    EXPECT_EXIT(std::copy(kFileContents, kFileContents + len, start),
                ::testing::KilledBySignal(SIGBUS), "");
  } else {
    // Read beyond first page.
    std::vector<char> in(kPageSize);
    EXPECT_EXIT(std::copy(start, start + kPageSize, in.data()),
                ::testing::KilledBySignal(SIGBUS), "");
  }
}

// Tests that SIGBUS is not raised when reading or writing to a file-mapped
// page before EOF, even if part of the mapping extends beyond EOF.
//
// See b/27877699.
TEST_P(MMapFileParamTest, NoSigBusOnPagesBeforeEOF) {
  SKIP_IF(!FSSupportsMap());
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, 2 * kPageSize, prot(), flags(), fd_.get(), 0),
              SyscallSucceeds());

  // The test passes if this survives.
  auto* start = reinterpret_cast<volatile char*>(addr);
  size_t len = strlen(kFileContents);
  if (prot() & PROT_WRITE) {
    std::copy(kFileContents, kFileContents + len, start);
  } else {
    std::vector<char> in(len);
    std::copy(start, start + len, in.data());
  }
}

// Tests that SIGBUS is not raised when reading or writing from a file-mapped
// page containing EOF, *after* the EOF.
TEST_P(MMapFileParamTest, NoSigBusOnPageContainingEOF) {
  SKIP_IF(!FSSupportsMap());
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, 2 * kPageSize, prot(), flags(), fd_.get(), 0),
              SyscallSucceeds());

  // The test passes if this survives. (Technically addr+kPageSize/2 is already
  // beyond EOF, but +1 to check for fencepost errors.)
  auto* start = reinterpret_cast<volatile char*>(addr + (kPageSize / 2) + 1);
  size_t len = strlen(kFileContents);
  if (prot() & PROT_WRITE) {
    std::copy(kFileContents, kFileContents + len, start);
  } else {
    std::vector<char> in(len);
    std::copy(start, start + len, in.data());
  }
}

// Tests that reading from writable shared file-mapped pages succeeds.
//
// On most platforms this is trivial, but when the file is mapped via the sentry
// page cache (which does not yet support writing to shared mappings), a bug
// caused reads to fail unnecessarily on such mappings. See b/28913513.
TEST_F(MMapFileTest, ReadingWritableSharedFilePageSucceeds) {
  SKIP_IF(!FSSupportsMap());
  uintptr_t addr;
  size_t len = strlen(kFileContents);

  ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd_.get(), 0),
              SyscallSucceeds());

  std::vector<char> buf(kPageSize);
  // The test passes if this survives.
  std::copy(reinterpret_cast<volatile char*>(addr),
            reinterpret_cast<volatile char*>(addr) + len, buf.data());
}

// Tests that EFAULT is returned when invoking a syscall that requires the OS to
// read past end of file (resulting in a fault in sentry context in the gVisor
// case). See b/28913513.
TEST_F(MMapFileTest, InternalSigBus) {
  SKIP_IF(!FSSupportsMap());
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE,
                         fd_.get(), 0),
              SyscallSucceeds());

  // This depends on the fact that gVisor implements pipes internally.
  int pipefd[2];
  ASSERT_THAT(pipe(pipefd), SyscallSucceeds());
  EXPECT_THAT(
      write(pipefd[1], reinterpret_cast<void*>(addr + kPageSize), kPageSize),
      SyscallFailsWithErrno(EFAULT));

  EXPECT_THAT(close(pipefd[0]), SyscallSucceeds());
  EXPECT_THAT(close(pipefd[1]), SyscallSucceeds());
}

// Like InternalSigBus, but test the WriteZerosAt path by reading from
// /dev/zero to a shared mapping (so that the SIGBUS isn't caught during
// copy-on-write breaking).
TEST_F(MMapFileTest, InternalSigBusZeroing) {
  SKIP_IF(!FSSupportsMap());
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd_.get(), 0),
              SyscallSucceeds());

  const FileDescriptor dev_zero =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY));
  EXPECT_THAT(read(dev_zero.get(), reinterpret_cast<void*>(addr + kPageSize),
                   kPageSize),
              SyscallFailsWithErrno(EFAULT));
}

// Checks that mmaps with a length of uint64_t(-PAGE_SIZE + 1) or greater do not
// induce a sentry panic (due to "rounding up" to 0).
TEST_F(MMapTest, HugeLength) {
  EXPECT_THAT(Map(0, static_cast<uint64_t>(-kPageSize + 1), PROT_NONE,
                  MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallFailsWithErrno(ENOMEM));
}

// Tests for a specific gVisor MM caching bug.
TEST_F(MMapTest, AccessCOWInvalidatesCachedSegments) {
  auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
  auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR));
  auto zero_fd = ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/zero", O_RDONLY));

  // Get a two-page private mapping and fill it with 1s.
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, 2 * kPageSize, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
              SyscallSucceeds());
  memset(addr_, 1, 2 * kPageSize);
  MaybeSave();

  // Fork to make the mapping copy-on-write.
  pid_t const pid = fork();
  if (pid == 0) {
    // The child process waits for the parent to SIGKILL it.
    while (true) {
      pause();
    }
  }
  ASSERT_THAT(pid, SyscallSucceeds());
  auto cleanup_child = Cleanup([&] {
    EXPECT_THAT(kill(pid, SIGKILL), SyscallSucceeds());
    int status;
    EXPECT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid));
  });

  // Induce a read-only Access of the first page of the mapping, which will not
  // cause a copy. The usermem.Segment should be cached.
  ASSERT_THAT(PwriteFd(fd.get(), addr_, kPageSize, 0),
              SyscallSucceedsWithValue(kPageSize));

  // Induce a writable Access of both pages of the mapping. This should
  // invalidate the cached Segment.
  ASSERT_THAT(PreadFd(zero_fd.get(), addr_, 2 * kPageSize, 0),
              SyscallSucceedsWithValue(2 * kPageSize));

  // Induce a read-only Access of the first page of the mapping again. It should
  // read the 0s that were stored in the mapping by the read from /dev/zero. If
  // the read failed to invalidate the cached Segment, it will instead read the
  // 1s in the stale page.
  ASSERT_THAT(PwriteFd(fd.get(), addr_, kPageSize, 0),
              SyscallSucceedsWithValue(kPageSize));
  std::vector<char> buf(kPageSize);
  ASSERT_THAT(PreadFd(fd.get(), buf.data(), kPageSize, 0),
              SyscallSucceedsWithValue(kPageSize));
  for (size_t i = 0; i < kPageSize; i++) {
    ASSERT_EQ(0, buf[i]) << "at offset " << i;
  }
}

TEST_F(MMapTest, NoReserve) {
  const size_t kSize = 10 * 1 << 20;  // 10M
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kSize, PROT_READ | PROT_WRITE,
                         MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0),
              SyscallSucceeds());
  EXPECT_GT(addr, 0);

  // Check that every page can be read/written. Technically, writing to memory
  // could SIGSEGV in case there is no more memory available. In gVisor it
  // would never happen though because NORESERVE is ignored. In Linux, it's
  // possible to fail, but allocation is small enough that it's highly likely
  // to succeed.
  for (size_t j = 0; j < kSize; j += kPageSize) {
    EXPECT_EQ(0, reinterpret_cast<char*>(addr)[j]);
    reinterpret_cast<char*>(addr)[j] = j;
  }
}

// Map more than the gVisor page-cache map unit (64k) and ensure that
// it is consistent with reading from the file.
TEST_F(MMapFileTest, Bug38498194) {
  SKIP_IF(!FSSupportsMap());
  // Choose a sufficiently large map unit.
  constexpr int kSize = 4 * 1024 * 1024;
  EXPECT_THAT(ftruncate(fd_.get(), kSize), SyscallSucceeds());

  // Map a large enough region so that multiple internal segments
  // are created to back the mapping.
  uintptr_t addr;
  ASSERT_THAT(
      addr = Map(0, kSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd_.get(), 0),
      SyscallSucceeds());

  std::vector<char> expect(kSize, 'a');
  std::copy(expect.data(), expect.data() + expect.size(),
            reinterpret_cast<volatile char*>(addr));

  // Trigger writeback for gVisor. In Linux pages stay cached until
  // it can't hold onto them anymore.
  ASSERT_THAT(Unmap(), SyscallSucceeds());

  std::vector<char> buf(kSize);
  ASSERT_THAT(Read(buf.data(), buf.size()),
              SyscallSucceedsWithValue(buf.size()));
  EXPECT_EQ(buf, expect) << std::string(buf.data(), buf.size());
}

// Tests that reading from a file to a memory mapping of the same file does not
// deadlock. See b/34813270.
TEST_F(MMapFileTest, SelfRead) {
  SKIP_IF(!FSSupportsMap());
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
                         fd_.get(), 0),
              SyscallSucceeds());
  EXPECT_THAT(Read(reinterpret_cast<char*>(addr), kPageSize / 2),
              SyscallSucceedsWithValue(kPageSize / 2));
  // The resulting file contents are poorly-specified and irrelevant.
}

// Tests that writing to a file from a memory mapping of the same file does not
// deadlock. Regression test for b/34813270.
TEST_F(MMapFileTest, SelfWrite) {
  SKIP_IF(!FSSupportsMap());
  uintptr_t addr;
  ASSERT_THAT(addr = Map(0, kPageSize, PROT_READ, MAP_SHARED, fd_.get(), 0),
              SyscallSucceeds());
  EXPECT_THAT(Write(reinterpret_cast<char*>(addr), kPageSize / 2),
              SyscallSucceedsWithValue(kPageSize / 2));
  // The resulting file contents are poorly-specified and irrelevant.
}

TEST(MMapDeathTest, TruncateAfterCOWBreak) {
  SetupGvisorDeathTest();

  // Create and map a single-page file.
  auto const temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
  auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(temp_file.path(), O_RDWR));
  ASSERT_THAT(ftruncate(fd.get(), kPageSize), SyscallSucceeds());

  auto maybe_mapping = Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE,
                            MAP_PRIVATE, fd.get(), 0);
  // Does FS support mmap?
  SKIP_IF(maybe_mapping.error().errno_value() == ENODEV);
  auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(std::move(maybe_mapping));

  // Write to this mapping, causing the page to be copied for write.
  memset(mapping.ptr(), 'a', mapping.len());
  MaybeSave();  // Trigger a co-operative save cycle.

  // Truncate the file and expect it to invalidate the copied page.
  ASSERT_THAT(ftruncate(fd.get(), 0), SyscallSucceeds());
  EXPECT_EXIT(*reinterpret_cast<volatile char*>(mapping.ptr()),
              ::testing::KilledBySignal(SIGBUS), "");
}

// Regression test for #147.
TEST(MMapNoFixtureTest, MapReadOnlyAfterCreateWriteOnly) {
  std::string filename = NewTempAbsPath();

  // We have to create the file O_RDONLY to reproduce the bug because
  // fsgofer.localFile.Create() silently upgrades O_WRONLY to O_RDWR, causing
  // the cached "write-only" FD to be read/write and therefore usable by mmap().
  auto const ro_fd = ASSERT_NO_ERRNO_AND_VALUE(
      Open(filename, O_RDONLY | O_CREAT | O_EXCL, 0666));

  // Get a write-only FD for the same file, which should be ignored by mmap()
  // (but isn't in #147).
  auto const wo_fd = ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_WRONLY));
  ASSERT_THAT(ftruncate(wo_fd.get(), kPageSize), SyscallSucceeds());

  auto maybe_mapping =
      Mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, ro_fd.get(), 0);
  // Does FS support mmap?
  SKIP_IF(maybe_mapping.error().errno_value() == ENODEV);
  auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(std::move(maybe_mapping));

  std::vector<char> buf(kPageSize);
  // The test passes if this survives.
  std::copy(static_cast<char*>(mapping.ptr()),
            static_cast<char*>(mapping.endptr()), buf.data());
}

// Conditional on MAP_32BIT.
// This flag is supported only on x86-64, for 64-bit programs.
#ifdef __x86_64__

TEST(MMapNoFixtureTest, Map32Bit) {
  auto const mapping = ASSERT_NO_ERRNO_AND_VALUE(
      MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE | MAP_32BIT));
  EXPECT_LT(mapping.addr(), static_cast<uintptr_t>(1) << 32);
  EXPECT_LE(mapping.endaddr(), static_cast<uintptr_t>(1) << 32);
}

#endif  // defined(__x86_64__)

INSTANTIATE_TEST_SUITE_P(
    ReadWriteSharedPrivate, MMapFileParamTest,
    ::testing::Combine(::testing::ValuesIn({
                           PROT_READ,
                           PROT_WRITE,
                           PROT_READ | PROT_WRITE,
                       }),
                       ::testing::ValuesIn({MAP_SHARED, MAP_PRIVATE})));

}  // namespace

}  // namespace testing
}  // namespace gvisor
