diff options
Diffstat (limited to 'test/syscalls/linux/madvise.cc')
-rw-r--r-- | test/syscalls/linux/madvise.cc | 110 |
1 files changed, 110 insertions, 0 deletions
diff --git a/test/syscalls/linux/madvise.cc b/test/syscalls/linux/madvise.cc index f6ad4d18b..08ff4052c 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,115 @@ 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 |