github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/syscalls/linux/mremap.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 <errno.h>
    16  #include <string.h>
    17  #include <sys/mman.h>
    18  
    19  #include <string>
    20  
    21  #include "gmock/gmock.h"
    22  #include "absl/strings/string_view.h"
    23  #include "test/util/file_descriptor.h"
    24  #include "test/util/logging.h"
    25  #include "test/util/memory_util.h"
    26  #include "test/util/multiprocess_util.h"
    27  #include "test/util/posix_error.h"
    28  #include "test/util/temp_path.h"
    29  #include "test/util/test_util.h"
    30  
    31  using ::testing::_;
    32  
    33  namespace gvisor {
    34  namespace testing {
    35  
    36  namespace {
    37  
    38  // Fixture for mremap tests parameterized by mmap flags.
    39  using MremapParamTest = ::testing::TestWithParam<int>;
    40  
    41  TEST_P(MremapParamTest, Noop) {
    42    Mapping const m =
    43        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
    44  
    45    ASSERT_THAT(Mremap(m.ptr(), kPageSize, kPageSize, 0, nullptr),
    46                IsPosixErrorOkAndHolds(m.ptr()));
    47    EXPECT_TRUE(IsMapped(m.addr()));
    48  }
    49  
    50  TEST_P(MremapParamTest, InPlace_ShrinkingWholeVMA) {
    51    Mapping const m =
    52        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
    53  
    54    const auto rest = [&] {
    55      // N.B. we must be in a single-threaded subprocess to ensure a
    56      // background thread doesn't concurrently map the second page.
    57      void* addr = mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, nullptr);
    58      TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
    59      TEST_CHECK(addr == m.ptr());
    60      MaybeSave();
    61  
    62      TEST_CHECK(IsMapped(m.addr()));
    63      TEST_CHECK(!IsMapped(m.addr() + kPageSize));
    64    };
    65  
    66    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
    67  }
    68  
    69  TEST_P(MremapParamTest, InPlace_ShrinkingPartialVMA) {
    70    Mapping const m =
    71        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam()));
    72  
    73    const auto rest = [&] {
    74      void* addr = mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, nullptr);
    75      TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
    76      TEST_CHECK(addr == m.ptr());
    77      MaybeSave();
    78  
    79      TEST_CHECK(IsMapped(m.addr()));
    80      TEST_CHECK(!IsMapped(m.addr() + kPageSize));
    81      TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize));
    82    };
    83  
    84    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
    85  }
    86  
    87  TEST_P(MremapParamTest, InPlace_ShrinkingAcrossVMAs) {
    88    Mapping const m =
    89        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_READ, GetParam()));
    90    // Changing permissions on the first page forces it to become a separate vma.
    91    ASSERT_THAT(mprotect(m.ptr(), kPageSize, PROT_NONE), SyscallSucceeds());
    92  
    93    const auto rest = [&] {
    94      // Both old_size and new_size now span two vmas; mremap
    95      // shouldn't care.
    96      void* addr = mremap(m.ptr(), 3 * kPageSize, 2 * kPageSize, 0, nullptr);
    97      TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
    98      TEST_CHECK(addr == m.ptr());
    99      MaybeSave();
   100  
   101      TEST_CHECK(IsMapped(m.addr()));
   102      TEST_CHECK(IsMapped(m.addr() + kPageSize));
   103      TEST_CHECK(!IsMapped(m.addr() + 2 * kPageSize));
   104    };
   105  
   106    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   107  }
   108  
   109  TEST_P(MremapParamTest, InPlace_ExpansionSuccess) {
   110    Mapping const m =
   111        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
   112  
   113    const auto rest = [&] {
   114      // Unmap the second page so that the first can be expanded back into it.
   115      //
   116      // N.B. we must be in a single-threaded subprocess to ensure a
   117      // background thread doesn't concurrently map this page.
   118      TEST_PCHECK(
   119          munmap(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize) == 0);
   120      MaybeSave();
   121  
   122      void* addr = mremap(m.ptr(), kPageSize, 2 * kPageSize, 0, nullptr);
   123      TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
   124      TEST_CHECK(addr == m.ptr());
   125      MaybeSave();
   126  
   127      TEST_CHECK(IsMapped(m.addr()));
   128      TEST_CHECK(IsMapped(m.addr() + kPageSize));
   129    };
   130  
   131    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   132  }
   133  
   134  TEST_P(MremapParamTest, InPlace_ExpansionFailure) {
   135    Mapping const m =
   136        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam()));
   137  
   138    const auto rest = [&] {
   139      // Unmap the second page, leaving a one-page hole. Trying to expand the
   140      // first page to three pages should fail since the original third page
   141      // is still mapped.
   142      TEST_PCHECK(
   143          munmap(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize) == 0);
   144      MaybeSave();
   145  
   146      void* addr = mremap(m.ptr(), kPageSize, 3 * kPageSize, 0, nullptr);
   147      TEST_CHECK_MSG(addr == MAP_FAILED, "mremap unexpectedly succeeded");
   148      TEST_PCHECK_MSG(errno == ENOMEM, "mremap failed with wrong errno");
   149      MaybeSave();
   150  
   151      TEST_CHECK(IsMapped(m.addr()));
   152      TEST_CHECK(!IsMapped(m.addr() + kPageSize));
   153      TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize));
   154    };
   155  
   156    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   157  }
   158  
   159  TEST_P(MremapParamTest, MayMove_Expansion) {
   160    Mapping const m =
   161        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam()));
   162  
   163    const auto rest = [&] {
   164      // Unmap the second page, leaving a one-page hole. Trying to expand the
   165      // first page to three pages with MREMAP_MAYMOVE should force the
   166      // mapping to be relocated since the original third page is still
   167      // mapped.
   168      TEST_PCHECK(
   169          munmap(reinterpret_cast<void*>(m.addr() + kPageSize), kPageSize) == 0);
   170      MaybeSave();
   171  
   172      void* addr2 =
   173          mremap(m.ptr(), kPageSize, 3 * kPageSize, MREMAP_MAYMOVE, nullptr);
   174      TEST_PCHECK_MSG(addr2 != MAP_FAILED, "mremap failed");
   175      MaybeSave();
   176  
   177      const Mapping m2 = Mapping(addr2, 3 * kPageSize);
   178      TEST_CHECK(m.addr() != m2.addr());
   179  
   180      TEST_CHECK(!IsMapped(m.addr()));
   181      TEST_CHECK(!IsMapped(m.addr() + kPageSize));
   182      TEST_CHECK(IsMapped(m.addr() + 2 * kPageSize));
   183      TEST_CHECK(IsMapped(m2.addr()));
   184      TEST_CHECK(IsMapped(m2.addr() + kPageSize));
   185      TEST_CHECK(IsMapped(m2.addr() + 2 * kPageSize));
   186    };
   187  
   188    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   189  }
   190  
   191  TEST_P(MremapParamTest, Fixed_SourceAndDestinationCannotOverlap) {
   192    Mapping const m =
   193        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
   194  
   195    ASSERT_THAT(Mremap(m.ptr(), kPageSize, kPageSize,
   196                       MREMAP_MAYMOVE | MREMAP_FIXED, m.ptr()),
   197                PosixErrorIs(EINVAL, _));
   198    EXPECT_TRUE(IsMapped(m.addr()));
   199  }
   200  
   201  TEST_P(MremapParamTest, Fixed_SameSize) {
   202    Mapping const src =
   203        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
   204    Mapping const dst =
   205        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
   206  
   207    const auto rest = [&] {
   208      // Unmap dst to create a hole.
   209      TEST_PCHECK(munmap(dst.ptr(), kPageSize) == 0);
   210      MaybeSave();
   211  
   212      void* addr = mremap(src.ptr(), kPageSize, kPageSize,
   213                          MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
   214      TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
   215      TEST_CHECK(addr == dst.ptr());
   216      MaybeSave();
   217  
   218      TEST_CHECK(!IsMapped(src.addr()));
   219      TEST_CHECK(IsMapped(dst.addr()));
   220    };
   221  
   222    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   223  }
   224  
   225  TEST_P(MremapParamTest, Fixed_SameSize_Unmapping) {
   226    // Like the Fixed_SameSize case, but expect mremap to unmap the destination
   227    // automatically.
   228    Mapping const src =
   229        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
   230    Mapping const dst =
   231        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
   232  
   233    const auto rest = [&] {
   234      void* addr = mremap(src.ptr(), kPageSize, kPageSize,
   235                          MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
   236      TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
   237      TEST_CHECK(addr == dst.ptr());
   238      MaybeSave();
   239  
   240      TEST_CHECK(!IsMapped(src.addr()));
   241      TEST_CHECK(IsMapped(dst.addr()));
   242    };
   243  
   244    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   245  }
   246  
   247  TEST_P(MremapParamTest, Fixed_ShrinkingWholeVMA) {
   248    Mapping const src =
   249        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
   250    Mapping const dst =
   251        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
   252  
   253    const auto rest = [&] {
   254      // Unmap dst so we can check that mremap does not keep the
   255      // second page.
   256      TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0);
   257      MaybeSave();
   258  
   259      void* addr = mremap(src.ptr(), 2 * kPageSize, kPageSize,
   260                          MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
   261      TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
   262      TEST_CHECK(addr == dst.ptr());
   263      MaybeSave();
   264  
   265      TEST_CHECK(!IsMapped(src.addr()));
   266      TEST_CHECK(!IsMapped(src.addr() + kPageSize));
   267      TEST_CHECK(IsMapped(dst.addr()));
   268      TEST_CHECK(!IsMapped(dst.addr() + kPageSize));
   269    };
   270  
   271    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   272  }
   273  
   274  TEST_P(MremapParamTest, Fixed_ShrinkingPartialVMA) {
   275    Mapping const src =
   276        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_NONE, GetParam()));
   277    Mapping const dst =
   278        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
   279  
   280    const auto rest = [&] {
   281      // Unmap dst so we can check that mremap does not keep the
   282      // second page.
   283      TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0);
   284      MaybeSave();
   285  
   286      void* addr = mremap(src.ptr(), 2 * kPageSize, kPageSize,
   287                          MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
   288      TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
   289      TEST_CHECK(addr == dst.ptr());
   290      MaybeSave();
   291  
   292      TEST_CHECK(!IsMapped(src.addr()));
   293      TEST_CHECK(!IsMapped(src.addr() + kPageSize));
   294      TEST_CHECK(IsMapped(src.addr() + 2 * kPageSize));
   295      TEST_CHECK(IsMapped(dst.addr()));
   296      TEST_CHECK(!IsMapped(dst.addr() + kPageSize));
   297    };
   298  
   299    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   300  }
   301  
   302  TEST_P(MremapParamTest, Fixed_ShrinkingAcrossVMAs) {
   303    Mapping const src =
   304        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(3 * kPageSize, PROT_READ, GetParam()));
   305    // Changing permissions on the first page forces it to become a separate vma.
   306    ASSERT_THAT(mprotect(src.ptr(), kPageSize, PROT_NONE), SyscallSucceeds());
   307    Mapping const dst =
   308        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
   309  
   310    const auto rest = [&] {
   311      // Unlike flags=0, MREMAP_FIXED requires that [old_address,
   312      // old_address+new_size) only spans a single vma.
   313      void* addr = mremap(src.ptr(), 3 * kPageSize, 2 * kPageSize,
   314                          MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
   315      TEST_CHECK_MSG(addr == MAP_FAILED, "mremap unexpectedly succeeded");
   316      TEST_PCHECK_MSG(errno == EFAULT, "mremap failed with wrong errno");
   317      MaybeSave();
   318  
   319      TEST_CHECK(IsMapped(src.addr()));
   320      TEST_CHECK(IsMapped(src.addr() + kPageSize));
   321      // Despite failing, mremap should have unmapped [old_address+new_size,
   322      // old_address+old_size) (i.e. the third page).
   323      TEST_CHECK(!IsMapped(src.addr() + 2 * kPageSize));
   324      // Despite failing, mremap should have unmapped the destination pages.
   325      TEST_CHECK(!IsMapped(dst.addr()));
   326      TEST_CHECK(!IsMapped(dst.addr() + kPageSize));
   327    };
   328  
   329    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   330  }
   331  
   332  TEST_P(MremapParamTest, Fixed_Expansion) {
   333    Mapping const src =
   334        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, GetParam()));
   335    Mapping const dst =
   336        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(2 * kPageSize, PROT_NONE, GetParam()));
   337  
   338    const auto rest = [&] {
   339      // Unmap dst so we can check that mremap actually maps all pages
   340      // at the destination.
   341      TEST_PCHECK(munmap(dst.ptr(), 2 * kPageSize) == 0);
   342      MaybeSave();
   343  
   344      void* addr = mremap(src.ptr(), kPageSize, 2 * kPageSize,
   345                          MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr());
   346      TEST_PCHECK_MSG(addr != MAP_FAILED, "mremap failed");
   347      TEST_CHECK(addr == dst.ptr());
   348      MaybeSave();
   349  
   350      TEST_CHECK(!IsMapped(src.addr()));
   351      TEST_CHECK(IsMapped(dst.addr()));
   352      TEST_CHECK(IsMapped(dst.addr() + kPageSize));
   353    };
   354  
   355    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   356  }
   357  
   358  INSTANTIATE_TEST_SUITE_P(PrivateShared, MremapParamTest,
   359                           ::testing::Values(MAP_PRIVATE, MAP_SHARED));
   360  
   361  // mremap with old_size == 0 only works with MAP_SHARED after Linux 4.14
   362  // (dba58d3b8c50 "mm/mremap: fail map duplication attempts for private
   363  // mappings").
   364  
   365  TEST(MremapTest, InPlace_Copy) {
   366    Mapping const m =
   367        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED));
   368    EXPECT_THAT(Mremap(m.ptr(), 0, kPageSize, 0, nullptr),
   369                PosixErrorIs(ENOMEM, _));
   370  }
   371  
   372  TEST(MremapTest, MayMove_Copy) {
   373    Mapping const m =
   374        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED));
   375  
   376    // Remainder of this test executes in a subprocess to ensure that if mremap
   377    // incorrectly removes m, it is not remapped by another thread.
   378    const auto rest = [&] {
   379      void* ptr = mremap(m.ptr(), 0, kPageSize, MREMAP_MAYMOVE, nullptr);
   380      MaybeSave();
   381      TEST_PCHECK_MSG(ptr != MAP_FAILED, "mremap failed");
   382      TEST_CHECK(ptr != m.ptr());
   383      TEST_CHECK(IsMapped(m.addr()));
   384      TEST_CHECK(IsMapped(reinterpret_cast<uintptr_t>(ptr)));
   385    };
   386    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   387  }
   388  
   389  TEST(MremapTest, MustMove_Copy) {
   390    Mapping const src =
   391        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_SHARED));
   392    Mapping const dst =
   393        ASSERT_NO_ERRNO_AND_VALUE(MmapAnon(kPageSize, PROT_NONE, MAP_PRIVATE));
   394  
   395    // Remainder of this test executes in a subprocess to ensure that if mremap
   396    // incorrectly removes src, it is not remapped by another thread.
   397    const auto rest = [&] {
   398      void* ptr = mremap(src.ptr(), 0, kPageSize, MREMAP_MAYMOVE | MREMAP_FIXED,
   399                         dst.ptr());
   400      MaybeSave();
   401      TEST_PCHECK_MSG(ptr != MAP_FAILED, "mremap failed");
   402      TEST_CHECK(ptr == dst.ptr());
   403      TEST_CHECK(IsMapped(src.addr()));
   404      TEST_CHECK(IsMapped(dst.addr()));
   405    };
   406    EXPECT_THAT(InForkedProcess(rest), IsPosixErrorOkAndHolds(0));
   407  }
   408  
   409  void ExpectAllBytesAre(absl::string_view v, char c) {
   410    for (size_t i = 0; i < v.size(); i++) {
   411      ASSERT_EQ(v[i], c) << "at offset " << i;
   412    }
   413  }
   414  
   415  TEST(MremapTest, ExpansionPreservesCOWPagesAndExposesNewFilePages) {
   416    // Create a file with 3 pages. The first is filled with 'a', the second is
   417    // filled with 'b', and the third is filled with 'c'.
   418    TempPath const file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   419    const FileDescriptor fd =
   420        ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR));
   421    ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'a').c_str(), kPageSize),
   422                SyscallSucceedsWithValue(kPageSize));
   423    ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'b').c_str(), kPageSize),
   424                SyscallSucceedsWithValue(kPageSize));
   425    ASSERT_THAT(WriteFd(fd.get(), std::string(kPageSize, 'c').c_str(), kPageSize),
   426                SyscallSucceedsWithValue(kPageSize));
   427  
   428    // Create a private mapping of the first 2 pages, and fill the second page
   429    // with 'd'.
   430    Mapping const src = ASSERT_NO_ERRNO_AND_VALUE(Mmap(nullptr, 2 * kPageSize,
   431                                                       PROT_READ | PROT_WRITE,
   432                                                       MAP_PRIVATE, fd.get(), 0));
   433    memset(reinterpret_cast<void*>(src.addr() + kPageSize), 'd', kPageSize);
   434    MaybeSave();
   435  
   436    // Move the mapping while expanding it to 3 pages. The resulting mapping
   437    // should contain the original first page of the file (filled with 'a'),
   438    // followed by the private copy of the second page (filled with 'd'), followed
   439    // by the newly-mapped third page of the file (filled with 'c').
   440    Mapping const dst = ASSERT_NO_ERRNO_AND_VALUE(
   441        MmapAnon(3 * kPageSize, PROT_NONE, MAP_PRIVATE));
   442    ASSERT_THAT(Mremap(src.ptr(), 2 * kPageSize, 3 * kPageSize,
   443                       MREMAP_MAYMOVE | MREMAP_FIXED, dst.ptr()),
   444                IsPosixErrorOkAndHolds(dst.ptr()));
   445    auto const v = dst.view();
   446    ExpectAllBytesAre(v.substr(0, kPageSize), 'a');
   447    ExpectAllBytesAre(v.substr(kPageSize, kPageSize), 'd');
   448    ExpectAllBytesAre(v.substr(2 * kPageSize, kPageSize), 'c');
   449  }
   450  
   451  TEST(MremapDeathTest, SharedAnon) {
   452    SetupGvisorDeathTest();
   453  
   454    // Reserve 4 pages of address space.
   455    Mapping const reserved = ASSERT_NO_ERRNO_AND_VALUE(
   456        MmapAnon(4 * kPageSize, PROT_NONE, MAP_PRIVATE));
   457  
   458    // Create a 2-page shared anonymous mapping at the beginning of the
   459    // reservation. Fill the first page with 'a' and the second with 'b'.
   460    Mapping const m = ASSERT_NO_ERRNO_AND_VALUE(
   461        Mmap(reserved.ptr(), 2 * kPageSize, PROT_READ | PROT_WRITE,
   462             MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0));
   463    memset(m.ptr(), 'a', kPageSize);
   464    memset(reinterpret_cast<void*>(m.addr() + kPageSize), 'b', kPageSize);
   465    MaybeSave();
   466  
   467    // Shrink the mapping to 1 page in-place.
   468    ASSERT_THAT(Mremap(m.ptr(), 2 * kPageSize, kPageSize, 0, m.ptr()),
   469                IsPosixErrorOkAndHolds(m.ptr()));
   470  
   471    // Expand the mapping to 3 pages, moving it forward by 1 page in the process
   472    // since the old and new mappings can't overlap.
   473    void* const new_m = reinterpret_cast<void*>(m.addr() + kPageSize);
   474    ASSERT_THAT(Mremap(m.ptr(), kPageSize, 3 * kPageSize,
   475                       MREMAP_MAYMOVE | MREMAP_FIXED, new_m),
   476                IsPosixErrorOkAndHolds(new_m));
   477  
   478    // The first 2 pages of the mapping should still contain the data we wrote
   479    // (i.e. shrinking should not have discarded the second page's data), while
   480    // touching the third page should raise SIGBUS.
   481    auto const v =
   482        absl::string_view(static_cast<char const*>(new_m), 3 * kPageSize);
   483    ExpectAllBytesAre(v.substr(0, kPageSize), 'a');
   484    ExpectAllBytesAre(v.substr(kPageSize, kPageSize), 'b');
   485    EXPECT_EXIT(ExpectAllBytesAre(v.substr(2 * kPageSize, kPageSize), '\0'),
   486                ::testing::KilledBySignal(SIGBUS), "");
   487  }
   488  
   489  }  // namespace
   490  
   491  }  // namespace testing
   492  }  // namespace gvisor