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