diff options
author | Neel Natu <neelnatu@google.com> | 2019-06-20 12:54:40 -0700 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2019-06-20 12:56:00 -0700 |
commit | 0b2135072d3a6b418f87f166b58dcf877f7c2fba (patch) | |
tree | 40c316afa59e5786fac24b7e8be940ae645ffb16 /test | |
parent | b46ec3704b60bebdd63a597c62f3f471ee0d9be9 (diff) |
Implement madvise(MADV_DONTFORK)
PiperOrigin-RevId: 254253777
Diffstat (limited to 'test')
-rw-r--r-- | test/syscalls/linux/BUILD | 1 | ||||
-rw-r--r-- | test/syscalls/linux/madvise.cc | 109 | ||||
-rw-r--r-- | test/syscalls/linux/mremap.cc | 11 | ||||
-rw-r--r-- | test/util/memory_util.h | 11 |
4 files changed, 121 insertions, 11 deletions
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 7e3ad08a9..0618fea58 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -985,6 +985,7 @@ cc_binary( "//test/util:file_descriptor", "//test/util:logging", "//test/util:memory_util", + "//test/util:multiprocess_util", "//test/util:posix_error", "//test/util:temp_path", "//test/util:test_main", diff --git a/test/syscalls/linux/madvise.cc b/test/syscalls/linux/madvise.cc index f6ad4d18b..352fcc6c4 100644 --- a/test/syscalls/linux/madvise.cc +++ b/test/syscalls/linux/madvise.cc @@ -29,6 +29,7 @@ #include "test/util/file_descriptor.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" @@ -136,6 +137,114 @@ TEST(MadviseDontneedTest, IgnoresPermissions) { EXPECT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds()); } +TEST(MadviseDontforkTest, AddressLength) { + auto m = + ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE)); + char *addr = static_cast<char *>(m.ptr()); + + // Address must be page aligned. + EXPECT_THAT(madvise(addr + 1, kPageSize, MADV_DONTFORK), + SyscallFailsWithErrno(EINVAL)); + + // Zero length madvise always succeeds. + EXPECT_THAT(madvise(addr, 0, MADV_DONTFORK), SyscallSucceeds()); + + // Length must not roll over after rounding up. + size_t badlen = std::numeric_limits<std::size_t>::max() - (kPageSize / 2); + EXPECT_THAT(madvise(0, badlen, MADV_DONTFORK), SyscallFailsWithErrno(EINVAL)); + + // Length need not be page aligned - it is implicitly rounded up. + EXPECT_THAT(madvise(addr, 1, MADV_DONTFORK), SyscallSucceeds()); + EXPECT_THAT(madvise(addr, kPageSize, MADV_DONTFORK), SyscallSucceeds()); +} + +TEST(MadviseDontforkTest, DontforkShared) { + // Mmap two shared file-backed pages and MADV_DONTFORK the second page. + TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( + /* parent = */ GetAbsoluteTestTmpdir(), + /* content = */ std::string(kPageSize * 2, 2), TempPath::kDefaultFileMode)); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR)); + + Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap( + nullptr, kPageSize * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0)); + + const Mapping ms1 = Mapping(reinterpret_cast<void *>(m.addr()), kPageSize); + const Mapping ms2 = + Mapping(reinterpret_cast<void *>(m.addr() + kPageSize), kPageSize); + m.release(); + + ASSERT_THAT(madvise(ms2.ptr(), kPageSize, MADV_DONTFORK), SyscallSucceeds()); + + const auto rest = [&] { + // First page is mapped in child and modifications are visible to parent + // via the shared mapping. + TEST_CHECK(IsMapped(ms1.addr())); + ExpectAllMappingBytes(ms1, 2); + memset(ms1.ptr(), 1, kPageSize); + ExpectAllMappingBytes(ms1, 1); + + // Second page must not be mapped in child. + TEST_CHECK(!IsMapped(ms2.addr())); + }; + + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); + + ExpectAllMappingBytes(ms1, 1); // page contents modified by child. + ExpectAllMappingBytes(ms2, 2); // page contents unchanged. +} + +TEST(MadviseDontforkTest, DontforkAnonPrivate) { + // Mmap three anonymous pages and MADV_DONTFORK the middle page. + Mapping m = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + const Mapping mp1 = Mapping(reinterpret_cast<void *>(m.addr()), kPageSize); + const Mapping mp2 = + Mapping(reinterpret_cast<void *>(m.addr() + kPageSize), kPageSize); + const Mapping mp3 = + Mapping(reinterpret_cast<void *>(m.addr() + 2 * kPageSize), kPageSize); + m.release(); + + ASSERT_THAT(madvise(mp2.ptr(), kPageSize, MADV_DONTFORK), SyscallSucceeds()); + + // Verify that all pages are zeroed and memset the first, second and third + // pages to 1, 2, and 3 respectively. + ExpectAllMappingBytes(mp1, 0); + memset(mp1.ptr(), 1, kPageSize); + + ExpectAllMappingBytes(mp2, 0); + memset(mp2.ptr(), 2, kPageSize); + + ExpectAllMappingBytes(mp3, 0); + memset(mp3.ptr(), 3, kPageSize); + + const auto rest = [&] { + // Verify first page is mapped, verify its contents and then modify the + // page. The mapping is private so the modifications are not visible to + // the parent. + TEST_CHECK(IsMapped(mp1.addr())); + ExpectAllMappingBytes(mp1, 1); + memset(mp1.ptr(), 11, kPageSize); + ExpectAllMappingBytes(mp1, 11); + + // Verify second page is not mapped. + TEST_CHECK(!IsMapped(mp2.addr())); + + // Verify third page is mapped, verify its contents and then modify the + // page. The mapping is private so the modifications are not visible to + // the parent. + TEST_CHECK(IsMapped(mp3.addr())); + ExpectAllMappingBytes(mp3, 3); + memset(mp3.ptr(), 13, kPageSize); + ExpectAllMappingBytes(mp3, 13); + }; + EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0)); + + // The fork and COW by child should not affect the parent mappings. + ExpectAllMappingBytes(mp1, 1); + ExpectAllMappingBytes(mp2, 2); + ExpectAllMappingBytes(mp3, 3); +} + } // namespace } // namespace testing diff --git a/test/syscalls/linux/mremap.cc b/test/syscalls/linux/mremap.cc index 7298d4ca8..64e435cb7 100644 --- a/test/syscalls/linux/mremap.cc +++ b/test/syscalls/linux/mremap.cc @@ -46,17 +46,6 @@ PosixErrorOr<void*> Mremap(void* old_address, size_t old_size, size_t new_size, return rv; } -// Returns true if the page containing addr is mapped. -bool IsMapped(uintptr_t addr) { - int const rv = msync(reinterpret_cast<void*>(addr & ~(kPageSize - 1)), - kPageSize, MS_ASYNC); - if (rv == 0) { - return true; - } - TEST_PCHECK_MSG(errno == ENOMEM, "msync failed with unexpected errno"); - return false; -} - // Fixture for mremap tests parameterized by mmap flags. using MremapParamTest = ::testing::TestWithParam<int>; diff --git a/test/util/memory_util.h b/test/util/memory_util.h index 8c77778ea..190c469b5 100644 --- a/test/util/memory_util.h +++ b/test/util/memory_util.h @@ -118,6 +118,17 @@ inline PosixErrorOr<Mapping> MmapAnon(size_t length, int prot, int flags) { return Mmap(nullptr, length, prot, flags | MAP_ANONYMOUS, -1, 0); } +// Returns true if the page containing addr is mapped. +inline bool IsMapped(uintptr_t addr) { + int const rv = msync(reinterpret_cast<void*>(addr & ~(kPageSize - 1)), + kPageSize, MS_ASYNC); + if (rv == 0) { + return true; + } + TEST_PCHECK_MSG(errno == ENOMEM, "msync failed with unexpected errno"); + return false; +} + } // namespace testing } // namespace gvisor |