// Copyright 2019 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/memfd.h>
#include <linux/unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/syscall.h>

#include <vector>

#include "gtest/gtest.h"
#include "test/util/file_descriptor.h"
#include "test/util/fs_util.h"
#include "test/util/memory_util.h"
#include "test/util/multiprocess_util.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"

namespace gvisor {
namespace testing {
namespace {

// The header sys/memfd.h isn't available on all systems, so redefining some of
// the constants here.
#define F_LINUX_SPECIFIC_BASE 1024

#ifndef F_ADD_SEALS
#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
#endif /* F_ADD_SEALS */

#ifndef F_GET_SEALS
#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10)
#endif /* F_GET_SEALS */

#define F_SEAL_SEAL 0x0001
#define F_SEAL_SHRINK 0x0002
#define F_SEAL_GROW 0x0004
#define F_SEAL_WRITE 0x0008

using ::gvisor::testing::IsTmpfs;
using ::testing::StartsWith;

const std::string kMemfdName = "some-memfd";

int memfd_create(const std::string& name, unsigned int flags) {
  return syscall(__NR_memfd_create, name.c_str(), flags);
}

PosixErrorOr<FileDescriptor> MemfdCreate(const std::string& name,
                                         uint32_t flags) {
  int fd = memfd_create(name, flags);
  if (fd < 0) {
    return PosixError(
        errno, absl::StrFormat("memfd_create(\"%s\", %#x)", name, flags));
  }
  MaybeSave();
  return FileDescriptor(fd);
}

// Procfs entries for memfds display the appropriate name.
TEST(MemfdTest, Name) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0));
  const std::string proc_name = ASSERT_NO_ERRNO_AND_VALUE(
      ReadLink(absl::StrFormat("/proc/self/fd/%d", memfd.get())));
  EXPECT_THAT(proc_name, StartsWith("/memfd:" + kMemfdName));
}

// Memfds support read/write syscalls.
TEST(MemfdTest, WriteRead) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0));

  // Write a random page of data to the memfd via write(2).
  std::vector<char> buf(kPageSize);
  RandomizeBuffer(buf.data(), buf.size());
  ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallSucceedsWithValue(kPageSize));

  // Read back the same data and verify.
  std::vector<char> buf2(kPageSize);
  ASSERT_THAT(lseek(memfd.get(), 0, SEEK_SET), SyscallSucceeds());
  EXPECT_THAT(read(memfd.get(), buf2.data(), buf2.size()),
              SyscallSucceedsWithValue(kPageSize));
  EXPECT_EQ(buf, buf2);
}

// Memfds can be mapped and used as usual.
TEST(MemfdTest, Mmap) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0));
  const Mapping m1 = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
      nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd.get(), 0));

  // Write a random page of data to the memfd via mmap m1.
  std::vector<char> buf(kPageSize);
  RandomizeBuffer(buf.data(), buf.size());
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds());
  memcpy(m1.ptr(), buf.data(), buf.size());

  // Read the data back via a read syscall on the memfd.
  std::vector<char> buf2(kPageSize);
  EXPECT_THAT(read(memfd.get(), buf2.data(), buf2.size()),
              SyscallSucceedsWithValue(kPageSize));
  EXPECT_EQ(buf, buf2);

  // The same data should be accessible via a new mapping m2.
  const Mapping m2 = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
      nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd.get(), 0));
  EXPECT_EQ(0, memcmp(m1.ptr(), m2.ptr(), kPageSize));
}

TEST(MemfdTest, DuplicateFDsShareContent) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0));
  const Mapping m1 = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
      nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd.get(), 0));
  const FileDescriptor memfd2 = ASSERT_NO_ERRNO_AND_VALUE(memfd.Dup());

  // Write a random page of data to the memfd via mmap m1.
  std::vector<char> buf(kPageSize);
  RandomizeBuffer(buf.data(), buf.size());
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds());
  memcpy(m1.ptr(), buf.data(), buf.size());

  // Read the data back via a read syscall on a duplicate fd.
  std::vector<char> buf2(kPageSize);
  EXPECT_THAT(read(memfd2.get(), buf2.data(), buf2.size()),
              SyscallSucceedsWithValue(kPageSize));
  EXPECT_EQ(buf, buf2);
}

// File seals are disabled by default on memfds.
TEST(MemfdTest, SealingDisabledByDefault) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0));
  EXPECT_THAT(fcntl(memfd.get(), F_GET_SEALS),
              SyscallSucceedsWithValue(F_SEAL_SEAL));
  // Attempting to set any seal should fail.
  EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE),
              SyscallFailsWithErrno(EPERM));
}

// Seals can be retrieved and updated for memfds.
TEST(MemfdTest, SealsGetSet) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  int seals;
  ASSERT_THAT(seals = fcntl(memfd.get(), F_GET_SEALS), SyscallSucceeds());
  // No seals are set yet.
  EXPECT_EQ(0, seals);

  // Set a seal and check that we can get it back.
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds());
  EXPECT_THAT(fcntl(memfd.get(), F_GET_SEALS),
              SyscallSucceedsWithValue(F_SEAL_WRITE));

  // Set some more seals and verify.
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK),
              SyscallSucceeds());
  EXPECT_THAT(
      fcntl(memfd.get(), F_GET_SEALS),
      SyscallSucceedsWithValue(F_SEAL_WRITE | F_SEAL_GROW | F_SEAL_SHRINK));

  // Attempting to set a seal that is already set is a no-op.
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds());
  EXPECT_THAT(
      fcntl(memfd.get(), F_GET_SEALS),
      SyscallSucceedsWithValue(F_SEAL_WRITE | F_SEAL_GROW | F_SEAL_SHRINK));

  // Add remaining seals and verify.
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_SEAL), SyscallSucceeds());
  EXPECT_THAT(fcntl(memfd.get(), F_GET_SEALS),
              SyscallSucceedsWithValue(F_SEAL_WRITE | F_SEAL_GROW |
                                       F_SEAL_SHRINK | F_SEAL_SEAL));
}

// F_SEAL_GROW prevents a memfd from being grown using ftruncate.
TEST(MemfdTest, SealGrowWithTruncate) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds());
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_GROW), SyscallSucceeds());

  // Try grow the memfd by 1 page.
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize * 2),
              SyscallFailsWithErrno(EPERM));

  // Ftruncate calls that don't actually grow the memfd are allowed.
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds());
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize / 2), SyscallSucceeds());

  // After shrinking, growing back is not allowed.
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallFailsWithErrno(EPERM));
}

// F_SEAL_GROW prevents a memfd from being grown using the write syscall.
TEST(MemfdTest, SealGrowWithWrite) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));

  // Initially, writing to the memfd succeeds.
  const std::vector<char> buf(kPageSize);
  EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallSucceedsWithValue(kPageSize));

  // Apply F_SEAL_GROW, subsequent writes which extend the memfd should fail.
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_GROW), SyscallSucceeds());
  EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallFailsWithErrno(EPERM));

  // However, zero-length writes are ok since they don't grow the memfd.
  EXPECT_THAT(write(memfd.get(), buf.data(), 0), SyscallSucceeds());

  // Writing to existing parts of the memfd is also ok.
  ASSERT_THAT(lseek(memfd.get(), 0, SEEK_SET), SyscallSucceeds());
  EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallSucceedsWithValue(kPageSize));

  // Returning the end of the file and writing still not allowed.
  EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallFailsWithErrno(EPERM));
}

// F_SEAL_GROW causes writes which partially extend off the current EOF to
// partially succeed, up to the page containing the EOF.
TEST(MemfdTest, SealGrowPartialWriteTruncated) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds());
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_GROW), SyscallSucceeds());

  // FD offset: 1 page, EOF: 1 page.

  ASSERT_THAT(lseek(memfd.get(), kPageSize * 3 / 4, SEEK_SET),
              SyscallSucceeds());

  // FD offset: 3/4 page. Writing a full page now should only write 1/4 page
  // worth of data. This partially succeeds because the first page is entirely
  // within the file and requires no growth, but attempting to write the final
  // 3/4 page would require growing the file.
  const std::vector<char> buf(kPageSize);

  // TODO(b/527998184): Determine what Linux change causes us to see EPERM.
  // gVisor should evolve to match the latest Linux behavior.
  bool is_new_linux = false;
  if (!IsRunningOnGvisor()) {
    auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion());
    if (version.major > 6 || (version.major == 6 && version.minor >= 12)) {
      is_new_linux = true;
    }
  }

  if (is_new_linux) {
    EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()),
                SyscallFailsWithErrno(EPERM));
  } else {
    EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()),
                SyscallSucceedsWithValue(kPageSize / 4));
  }
}

// F_SEAL_GROW causes writes which partially extend off the current EOF to fail
// in its entirety if the only data written would be to the page containing the
// EOF.
TEST(MemfdTest, SealGrowPartialWriteTruncatedSamePage) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize * 3 / 4), SyscallSucceeds());
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_GROW), SyscallSucceeds());

  // EOF: 3/4 page, writing 1/2 page starting at 1/2 page would cause the file
  // to grow. Since this would require only the page containing the EOF to be
  // modified, the write is rejected entirely.
  const std::vector<char> buf(kPageSize / 2);
  EXPECT_THAT(pwrite(memfd.get(), buf.data(), buf.size(), kPageSize / 2),
              SyscallFailsWithErrno(EPERM));

  // However, writing up to EOF is fine.
  EXPECT_THAT(pwrite(memfd.get(), buf.data(), buf.size() / 2, kPageSize / 2),
              SyscallSucceedsWithValue(kPageSize / 4));
}

// F_SEAL_SHRINK prevents a memfd from being shrunk using ftruncate.
TEST(MemfdTest, SealShrink) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds());
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_SHRINK),
              SyscallSucceeds());

  // Shrink by half a page.
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize / 2),
              SyscallFailsWithErrno(EPERM));

  // Ftruncate calls that don't actually shrink the file are allowed.
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallSucceeds());
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize * 2), SyscallSucceeds());

  // After growing, shrinking is still not allowed.
  ASSERT_THAT(ftruncate(memfd.get(), kPageSize), SyscallFailsWithErrno(EPERM));
}

// F_SEAL_WRITE prevents a memfd from being written to through a write
// syscall.
TEST(MemfdTest, SealWriteWithWrite) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  const std::vector<char> buf(kPageSize);
  ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallSucceedsWithValue(kPageSize));
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds());

  // Attempting to write at the end of the file fails.
  EXPECT_THAT(write(memfd.get(), buf.data(), 1), SyscallFailsWithErrno(EPERM));

  // Attempting to overwrite an existing part of the memfd fails.
  EXPECT_THAT(pwrite(memfd.get(), buf.data(), 1, 0),
              SyscallFailsWithErrno(EPERM));
  EXPECT_THAT(pwrite(memfd.get(), buf.data(), buf.size() / 2, kPageSize / 2),
              SyscallFailsWithErrno(EPERM));
  EXPECT_THAT(pwrite(memfd.get(), buf.data(), buf.size(), kPageSize / 2),
              SyscallFailsWithErrno(EPERM));

  // Zero-length writes however do not fail.
  EXPECT_THAT(write(memfd.get(), buf.data(), 0), SyscallSucceeds());
}

// F_SEAL_WRITE prevents a memfd from being written to through an mmap.
TEST(MemfdTest, SealWriteWithMmap) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  const std::vector<char> buf(kPageSize);
  ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallSucceedsWithValue(kPageSize));
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds());

  // Can't create a shared mapping with writes sealed.
  void* ret = mmap(nullptr, kPageSize, PROT_WRITE, MAP_SHARED, memfd.get(), 0);
  EXPECT_EQ(ret, MAP_FAILED);
  EXPECT_EQ(errno, EPERM);

  // Starting with Linux 6.7, PROT_READ, MAP_SHARED mappings on F_SEAL_WRITE
  // sealed memfds are allowed. This was unintentionally reverted in 6.12 and
  // fixed in 6.13. See kernel commit 8ec396d05d1b ("mm: reinstate ability to
  // map write-sealed memfd mappings read-only"). Note that this behavior might
  // be backported to older LTS kernels. We skip verifying this specific
  // behavior on kernels where support is known to be missing or ambiguous to
  // avoid test flakiness. gVisor implements the latest Linux behavior.
  bool check_read_shared = true;
  if (!IsRunningOnGvisor()) {
    auto version = ASSERT_NO_ERRNO_AND_VALUE(GetKernelVersion());
    if (version.major < 6 || (version.major == 6 && version.minor < 13)) {
      check_read_shared = false;
    }
  }
  if (check_read_shared) {
    void* ret_read =
        mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, memfd.get(), 0);
    EXPECT_NE(ret_read, MAP_FAILED);
    if (ret_read != MAP_FAILED) {
      munmap(ret_read, kPageSize);
    }
  }

  // However, private mappings are ok.
  EXPECT_NO_ERRNO(Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE,
                       memfd.get(), 0));
}

// Adding F_SEAL_WRITE fails when there are outstanding writable mappings to a
// memfd.
TEST(MemfdTest, SealWriteWithOutstandingWritbleMapping) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  const std::vector<char> buf(kPageSize);
  ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallSucceedsWithValue(kPageSize));

  // Attempting to add F_SEAL_WRITE with active shared mapping with any set of
  // permissions fails.

  // Read-only shared mapping.
  {
    const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
        Mmap(nullptr, kPageSize, PROT_READ, MAP_SHARED, memfd.get(), 0));
    EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE),
                SyscallFailsWithErrno(EBUSY));
  }

  // Write-only shared mapping.
  {
    const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
        Mmap(nullptr, kPageSize, PROT_WRITE, MAP_SHARED, memfd.get(), 0));
    EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE),
                SyscallFailsWithErrno(EBUSY));
  }

  // Read-write shared mapping.
  {
    const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
        Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED,
             memfd.get(), 0));
    EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE),
                SyscallFailsWithErrno(EBUSY));
  }

  // F_SEAL_WRITE can be set with private mappings with any permissions.
  {
    const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
        Mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE,
             memfd.get(), 0));
    EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE),
                SyscallSucceeds());
  }
}

// When applying F_SEAL_WRITE fails due to outstanding writable mappings, any
// additional seals passed to the same add seal call are also rejected.
TEST(MemfdTest, NoPartialSealApplicationWhenWriteSealRejected) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
      nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd.get(), 0));

  // Try add some seals along with F_SEAL_WRITE. The seal application should
  // fail since there exists an active shared mapping.
  EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE | F_SEAL_GROW),
              SyscallFailsWithErrno(EBUSY));

  // None of the seals should be applied.
  EXPECT_THAT(fcntl(memfd.get(), F_GET_SEALS), SyscallSucceedsWithValue(0));
}

// Seals are inode level properties, and apply to all file descriptors referring
// to a memfd.
TEST(MemfdTest, SealsAreInodeLevelProperties) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  const FileDescriptor memfd2 = ASSERT_NO_ERRNO_AND_VALUE(memfd.Dup());

  // Add seal through the original memfd, and verify that it appears on the
  // dupped fd.
  ASSERT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds());
  EXPECT_THAT(fcntl(memfd2.get(), F_GET_SEALS),
              SyscallSucceedsWithValue(F_SEAL_WRITE));

  // Verify the seal actually applies to both fds.
  std::vector<char> buf(kPageSize);
  EXPECT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallFailsWithErrno(EPERM));
  EXPECT_THAT(write(memfd2.get(), buf.data(), buf.size()),
              SyscallFailsWithErrno(EPERM));

  // Seals are enforced on new FDs that are dupped after the seal is already
  // applied.
  const FileDescriptor memfd3 = ASSERT_NO_ERRNO_AND_VALUE(memfd2.Dup());
  EXPECT_THAT(write(memfd3.get(), buf.data(), buf.size()),
              SyscallFailsWithErrno(EPERM));

  // Try a new seal applied to one of the dupped fds.
  ASSERT_THAT(fcntl(memfd3.get(), F_ADD_SEALS, F_SEAL_GROW), SyscallSucceeds());
  EXPECT_THAT(ftruncate(memfd.get(), kPageSize), SyscallFailsWithErrno(EPERM));
  EXPECT_THAT(ftruncate(memfd2.get(), kPageSize), SyscallFailsWithErrno(EPERM));
  EXPECT_THAT(ftruncate(memfd3.get(), kPageSize), SyscallFailsWithErrno(EPERM));
}

// Tmpfs files also support seals, but are created with F_SEAL_SEAL.
TEST(MemfdTest, TmpfsFilesHaveSealSeal) {
  std::string tmpdir = GetAbsoluteTestTmpdir();
  SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(tmpdir.c_str())));
  const TempPath tmpfs_file =
      ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(tmpdir.c_str()));
  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(tmpfs_file.path(), O_RDWR, 0644));
  EXPECT_THAT(fcntl(fd.get(), F_GET_SEALS),
              SyscallSucceedsWithValue(F_SEAL_SEAL));
}

// Can open a memfd from procfs and use as normal.
TEST(MemfdTest, CanOpenFromProcfs) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));

  // Write a random page of data to the memfd via write(2).
  std::vector<char> buf(kPageSize);
  RandomizeBuffer(buf.data(), buf.size());
  ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallSucceedsWithValue(kPageSize));

  // Read back the same data from the fd obtained from procfs and verify.
  const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(
      Open(absl::StrFormat("/proc/self/fd/%d", memfd.get()), O_RDWR));
  std::vector<char> buf2(kPageSize);
  EXPECT_THAT(pread(fd.get(), buf2.data(), buf2.size(), 0),
              SyscallSucceedsWithValue(kPageSize));
  EXPECT_EQ(buf, buf2);
}

// Test that memfd permissions are set up correctly to allow another process to
// open it from procfs.
TEST(MemfdTest, OtherProcessCanOpenFromProcfs) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));
  const auto memfd_path =
      absl::StrFormat("/proc/%d/fd/%d", getpid(), memfd.get());
  const auto rest = [&] {
    int fd = open(memfd_path.c_str(), O_RDWR);
    TEST_PCHECK(fd >= 0);
    TEST_PCHECK(close(fd) >= 0);
  };
  EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
}

// Test that only files opened as writable can have seals applied to them.
// Normally there's no way to specify file permissions on memfds, but we can
// obtain a read-only memfd by opening the corresponding procfs fd entry as
// read-only.
TEST(MemfdTest, MemfdMustBeWritableToModifySeals) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, MFD_ALLOW_SEALING));

  // Initially adding a seal works.
  EXPECT_THAT(fcntl(memfd.get(), F_ADD_SEALS, F_SEAL_WRITE), SyscallSucceeds());

  // Re-open the memfd as read-only from procfs.
  const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(
      Open(absl::StrFormat("/proc/self/fd/%d", memfd.get()), O_RDONLY));

  // Can't add seals through an unwritable fd.
  EXPECT_THAT(fcntl(fd.get(), F_ADD_SEALS, F_SEAL_GROW),
              SyscallFailsWithErrno(EPERM));
}

// Test that the memfd implementation internally tracks potentially writable
// maps correctly.
TEST(MemfdTest, MultipleWritableAndNonWritableRefsToSameFileRegion) {
  const FileDescriptor memfd =
      ASSERT_NO_ERRNO_AND_VALUE(MemfdCreate(kMemfdName, 0));

  // Populate with a random page of data.
  std::vector<char> buf(kPageSize);
  RandomizeBuffer(buf.data(), buf.size());
  ASSERT_THAT(write(memfd.get(), buf.data(), buf.size()),
              SyscallSucceedsWithValue(kPageSize));

  // Read-only map to the page. This should cause an initial mapping to be
  // created.
  Mapping m1 = ASSERT_NO_ERRNO_AND_VALUE(
      Mmap(nullptr, kPageSize, PROT_READ, MAP_PRIVATE, memfd.get(), 0));

  // Create a shared writable map to the page. This should cause the internal
  // mapping to become potentially writable.
  Mapping m2 = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
      nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, memfd.get(), 0));

  // Drop the read-only mapping first. If writable-ness isn't tracked correctly,
  // this can cause some misaccounting, which can trigger asserts internally.
  m1.reset();
  m2.reset();
}

}  // namespace
}  // namespace testing
}  // namespace gvisor
