gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/madvise.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <fcntl.h>
    16  #include <stdlib.h>
    17  #include <string.h>
    18  #include <sys/mman.h>
    19  #include <sys/stat.h>
    20  #include <sys/types.h>
    21  #include <sys/wait.h>
    22  #include <unistd.h>
    23  
    24  #include <string>
    25  
    26  #include "gmock/gmock.h"
    27  #include "gtest/gtest.h"
    28  #include "test/util/file_descriptor.h"
    29  #include "test/util/logging.h"
    30  #include "test/util/memory_util.h"
    31  #include "test/util/multiprocess_util.h"
    32  #include "test/util/posix_error.h"
    33  #include "test/util/temp_path.h"
    34  #include "test/util/test_util.h"
    35  
    36  namespace gvisor {
    37  namespace testing {
    38  
    39  namespace {
    40  
    41  void ExpectAllMappingBytes(Mapping const& m, char c) {
    42    auto const v = m.view();
    43    for (size_t i = 0; i < kPageSize; i++) {
    44      ASSERT_EQ(v[i], c) << "at offset " << i;
    45    }
    46  }
    47  
    48  // Equivalent to ExpectAllMappingBytes but async-signal-safe and with less
    49  // helpful failure messages.
    50  void CheckAllMappingBytes(Mapping const& m, char c) {
    51    auto const v = m.view();
    52    for (size_t i = 0; i < kPageSize; i++) {
    53      TEST_CHECK_MSG(v[i] == c, "mapping contains wrong value");
    54    }
    55  }
    56  
    57  TEST(MadviseDontneedTest, ZerosPrivateAnonPage) {
    58    auto m = ASSERT_NO_ERRNO_AND_VALUE(
    59        MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE));
    60    ExpectAllMappingBytes(m, 0);
    61    memset(m.ptr(), 1, m.len());
    62    ExpectAllMappingBytes(m, 1);
    63    ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds());
    64    ExpectAllMappingBytes(m, 0);
    65  }
    66  
    67  TEST(MadviseDontneedTest, ZerosCOWAnonPageInCallerOnly) {
    68    auto m = ASSERT_NO_ERRNO_AND_VALUE(
    69        MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE));
    70    ExpectAllMappingBytes(m, 0);
    71    memset(m.ptr(), 2, m.len());
    72    ExpectAllMappingBytes(m, 2);
    73  
    74    // Do madvise in a child process.
    75    pid_t pid = fork();
    76    CheckAllMappingBytes(m, 2);
    77    if (pid == 0) {
    78      TEST_PCHECK(madvise(m.ptr(), m.len(), MADV_DONTNEED) == 0);
    79      CheckAllMappingBytes(m, 0);
    80      _exit(0);
    81    }
    82  
    83    ASSERT_THAT(pid, SyscallSucceeds());
    84  
    85    int status = 0;
    86    ASSERT_THAT(waitpid(-1, &status, 0), SyscallSucceedsWithValue(pid));
    87    EXPECT_TRUE(WIFEXITED(status));
    88    EXPECT_EQ(WEXITSTATUS(status), 0);
    89    // The child's madvise should not have affected the parent.
    90    ExpectAllMappingBytes(m, 2);
    91  }
    92  
    93  TEST(MadviseDontneedTest, DoesNotModifySharedAnonPage) {
    94    auto m = ASSERT_NO_ERRNO_AND_VALUE(
    95        MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED));
    96    ExpectAllMappingBytes(m, 0);
    97    memset(m.ptr(), 3, m.len());
    98    ExpectAllMappingBytes(m, 3);
    99    ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds());
   100    ExpectAllMappingBytes(m, 3);
   101  }
   102  
   103  TEST(MadviseDontneedTest, CleansPrivateFilePage) {
   104    TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
   105        /* parent = */ GetAbsoluteTestTmpdir(),
   106        /* content = */ std::string(kPageSize, 4), TempPath::kDefaultFileMode));
   107    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR));
   108  
   109    Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
   110        nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd.get(), 0));
   111    ExpectAllMappingBytes(m, 4);
   112    memset(m.ptr(), 5, m.len());
   113    ExpectAllMappingBytes(m, 5);
   114    ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds());
   115    ExpectAllMappingBytes(m, 4);
   116  }
   117  
   118  TEST(MadviseDontneedTest, DoesNotModifySharedFilePage) {
   119    TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
   120        /* parent = */ GetAbsoluteTestTmpdir(),
   121        /* content = */ std::string(kPageSize, 6), TempPath::kDefaultFileMode));
   122    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR));
   123  
   124    Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
   125        nullptr, kPageSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0));
   126    ExpectAllMappingBytes(m, 6);
   127    memset(m.ptr(), 7, m.len());
   128    ExpectAllMappingBytes(m, 7);
   129    ASSERT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds());
   130    ExpectAllMappingBytes(m, 7);
   131  }
   132  
   133  TEST(MadviseDontneedTest, IgnoresPermissions) {
   134    auto m =
   135        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE));
   136    EXPECT_THAT(madvise(m.ptr(), m.len(), MADV_DONTNEED), SyscallSucceeds());
   137  }
   138  
   139  TEST(MadviseDontforkTest, AddressLength) {
   140    auto m =
   141        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE));
   142    char* addr = static_cast<char*>(m.ptr());
   143  
   144    // Address must be page aligned.
   145    EXPECT_THAT(madvise(addr + 1, kPageSize, MADV_DONTFORK),
   146                SyscallFailsWithErrno(EINVAL));
   147  
   148    // Zero length madvise always succeeds.
   149    EXPECT_THAT(madvise(addr, 0, MADV_DONTFORK), SyscallSucceeds());
   150  
   151    // Length must not roll over after rounding up.
   152    size_t badlen = std::numeric_limits<std::size_t>::max() - (kPageSize / 2);
   153    EXPECT_THAT(madvise(0, badlen, MADV_DONTFORK), SyscallFailsWithErrno(EINVAL));
   154  
   155    // Length need not be page aligned - it is implicitly rounded up.
   156    EXPECT_THAT(madvise(addr, 1, MADV_DONTFORK), SyscallSucceeds());
   157    EXPECT_THAT(madvise(addr, kPageSize, MADV_DONTFORK), SyscallSucceeds());
   158  }
   159  
   160  TEST(MadviseDontforkTest, DontforkShared) {
   161    // Mmap two shared file-backed pages and MADV_DONTFORK the second page.
   162    TempPath f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
   163        /* parent = */ GetAbsoluteTestTmpdir(),
   164        /* content = */ std::string(kPageSize * 2, 2),
   165        TempPath::kDefaultFileMode));
   166    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDWR));
   167  
   168    Mapping m = ASSERT_NO_ERRNO_AND_VALUE(Mmap(
   169        nullptr, kPageSize * 2, PROT_READ | PROT_WRITE, MAP_SHARED, fd.get(), 0));
   170  
   171    const Mapping ms1 = Mapping(reinterpret_cast<void*>(m.addr()), kPageSize);
   172    const Mapping ms2 =
   173        Mapping(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize);
   174    m.release();
   175  
   176    ASSERT_THAT(madvise(ms2.ptr(), kPageSize, MADV_DONTFORK), SyscallSucceeds());
   177  
   178    const auto rest = [&] {
   179      // First page is mapped in child and modifications are visible to parent
   180      // via the shared mapping.
   181      TEST_CHECK(IsMapped(ms1.addr()));
   182      CheckAllMappingBytes(ms1, 2);
   183      memset(ms1.ptr(), 1, kPageSize);
   184      CheckAllMappingBytes(ms1, 1);
   185  
   186      // Second page must not be mapped in child.
   187      TEST_CHECK(!IsMapped(ms2.addr()));
   188    };
   189  
   190    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   191  
   192    ExpectAllMappingBytes(ms1, 1);  // page contents modified by child.
   193    ExpectAllMappingBytes(ms2, 2);  // page contents unchanged.
   194  }
   195  
   196  TEST(MadviseDontforkTest, DontforkAnonPrivate) {
   197    // Mmap three anonymous pages and MADV_DONTFORK the middle page.
   198    Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
   199        MmapAnon(kPageSize * 3, PROT_READ | PROT_WRITE, MAP_PRIVATE));
   200    const Mapping mp1 = Mapping(reinterpret_cast<void*>(m.addr()), kPageSize);
   201    const Mapping mp2 =
   202        Mapping(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize);
   203    const Mapping mp3 =
   204        Mapping(reinterpret_cast<void*>(m.addr() + 2 * kPageSize), kPageSize);
   205    m.release();
   206  
   207    ASSERT_THAT(madvise(mp2.ptr(), kPageSize, MADV_DONTFORK), SyscallSucceeds());
   208  
   209    // Verify that all pages are zeroed and memset the first, second and third
   210    // pages to 1, 2, and 3 respectively.
   211    ExpectAllMappingBytes(mp1, 0);
   212    memset(mp1.ptr(), 1, kPageSize);
   213  
   214    ExpectAllMappingBytes(mp2, 0);
   215    memset(mp2.ptr(), 2, kPageSize);
   216  
   217    ExpectAllMappingBytes(mp3, 0);
   218    memset(mp3.ptr(), 3, kPageSize);
   219  
   220    const auto rest = [&] {
   221      // Verify first page is mapped, verify its contents and then modify the
   222      // page. The mapping is private so the modifications are not visible to
   223      // the parent.
   224      TEST_CHECK(IsMapped(mp1.addr()));
   225      CheckAllMappingBytes(mp1, 1);
   226      memset(mp1.ptr(), 11, kPageSize);
   227      CheckAllMappingBytes(mp1, 11);
   228  
   229      // Verify second page is not mapped.
   230      TEST_CHECK(!IsMapped(mp2.addr()));
   231  
   232      // Verify third page is mapped, verify its contents and then modify the
   233      // page. The mapping is private so the modifications are not visible to
   234      // the parent.
   235      TEST_CHECK(IsMapped(mp3.addr()));
   236      CheckAllMappingBytes(mp3, 3);
   237      memset(mp3.ptr(), 13, kPageSize);
   238      CheckAllMappingBytes(mp3, 13);
   239    };
   240    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   241  
   242    // The fork and COW by child should not affect the parent mappings.
   243    ExpectAllMappingBytes(mp1, 1);
   244    ExpectAllMappingBytes(mp2, 2);
   245    ExpectAllMappingBytes(mp3, 3);
   246  }
   247  
   248  }  // namespace
   249  
   250  }  // namespace testing
   251  }  // namespace gvisor