gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/shm.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 <stdio.h> 16 #include <sys/ipc.h> 17 #include <sys/mman.h> 18 #include <sys/shm.h> 19 #include <sys/types.h> 20 21 #include "absl/time/clock.h" 22 #include "test/util/multiprocess_util.h" 23 #include "test/util/posix_error.h" 24 #include "test/util/temp_path.h" 25 #include "test/util/test_util.h" 26 27 namespace gvisor { 28 namespace testing { 29 namespace { 30 31 using ::testing::_; 32 using ::testing::AnyOf; 33 using ::testing::Eq; 34 35 const uint64_t kAllocSize = kPageSize * 128ULL; 36 37 PosixErrorOr<char*> Shmat(int shmid, const void* shmaddr, int shmflg) { 38 const intptr_t addr = 39 reinterpret_cast<intptr_t>(shmat(shmid, shmaddr, shmflg)); 40 if (addr == -1) { 41 return PosixError(errno, "shmat() failed"); 42 } 43 return reinterpret_cast<char*>(addr); 44 } 45 46 PosixError Shmdt(const char* shmaddr) { 47 const int ret = shmdt(shmaddr); 48 if (ret == -1) { 49 return PosixError(errno, "shmdt() failed"); 50 } 51 return NoError(); 52 } 53 54 template <typename T> 55 PosixErrorOr<int> Shmctl(int shmid, int cmd, T* buf) { 56 int ret = shmctl(shmid, cmd, reinterpret_cast<struct shmid_ds*>(buf)); 57 if (ret == -1) { 58 return PosixError(errno, "shmctl() failed"); 59 } 60 return ret; 61 } 62 63 // ShmSegment is a RAII object for automatically cleaning up shm segments. 64 class ShmSegment { 65 public: 66 explicit ShmSegment(int id) : id_(id) {} 67 68 ~ShmSegment() { 69 if (id_ >= 0) { 70 EXPECT_NO_ERRNO(Rmid()); 71 id_ = -1; 72 } 73 } 74 75 ShmSegment(ShmSegment&& other) : id_(other.release()) {} 76 77 ShmSegment& operator=(ShmSegment&& other) { 78 id_ = other.release(); 79 return *this; 80 } 81 82 ShmSegment(ShmSegment const& other) = delete; 83 ShmSegment& operator=(ShmSegment const& other) = delete; 84 85 int id() const { return id_; } 86 87 int release() { 88 int id = id_; 89 id_ = -1; 90 return id; 91 } 92 93 PosixErrorOr<int> Rmid() { 94 RETURN_IF_ERRNO(Shmctl<void>(id_, IPC_RMID, nullptr)); 95 return release(); 96 } 97 98 private: 99 int id_ = -1; 100 }; 101 102 PosixErrorOr<int> ShmgetRaw(key_t key, size_t size, int shmflg) { 103 int id = shmget(key, size, shmflg); 104 if (id == -1) { 105 return PosixError(errno, "shmget() failed"); 106 } 107 return id; 108 } 109 110 PosixErrorOr<ShmSegment> Shmget(key_t key, size_t size, int shmflg) { 111 ASSIGN_OR_RETURN_ERRNO(int id, ShmgetRaw(key, size, shmflg)); 112 return ShmSegment(id); 113 } 114 115 TEST(ShmTest, AttachDetach) { 116 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 117 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 118 struct shmid_ds attr; 119 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); 120 EXPECT_EQ(attr.shm_segsz, kAllocSize); 121 EXPECT_EQ(attr.shm_nattch, 0); 122 123 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 124 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); 125 EXPECT_EQ(attr.shm_nattch, 1); 126 127 const char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 128 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); 129 EXPECT_EQ(attr.shm_nattch, 2); 130 131 ASSERT_NO_ERRNO(Shmdt(addr)); 132 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); 133 EXPECT_EQ(attr.shm_nattch, 1); 134 135 ASSERT_NO_ERRNO(Shmdt(addr2)); 136 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); 137 EXPECT_EQ(attr.shm_nattch, 0); 138 } 139 140 TEST(ShmTest, LookupByKey) { 141 const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 142 const key_t key = ftok(keyfile.path().c_str(), 1); 143 const ShmSegment shm = 144 ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); 145 const int id2 = ASSERT_NO_ERRNO_AND_VALUE(ShmgetRaw(key, kAllocSize, 0777)); 146 EXPECT_EQ(shm.id(), id2); 147 } 148 149 TEST(ShmTest, DetachedSegmentsPersist) { 150 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 151 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 152 char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 153 addr[0] = 'x'; 154 ASSERT_NO_ERRNO(Shmdt(addr)); 155 156 // We should be able to re-attach to the same segment and get our data back. 157 addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 158 EXPECT_EQ(addr[0], 'x'); 159 ASSERT_NO_ERRNO(Shmdt(addr)); 160 } 161 162 TEST(ShmTest, MultipleDetachFails) { 163 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 164 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 165 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 166 ASSERT_NO_ERRNO(Shmdt(addr)); 167 EXPECT_THAT(Shmdt(addr), PosixErrorIs(EINVAL, _)); 168 } 169 170 TEST(ShmTest, IpcStat) { 171 const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 172 const key_t key = ftok(keyfile.path().c_str(), 1); 173 174 const time_t start = time(nullptr); 175 176 const ShmSegment shm = 177 ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); 178 179 const uid_t uid = getuid(); 180 const gid_t gid = getgid(); 181 const pid_t pid = getpid(); 182 183 struct shmid_ds attr; 184 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); 185 186 EXPECT_EQ(attr.shm_perm.__key, key); 187 EXPECT_EQ(attr.shm_perm.uid, uid); 188 EXPECT_EQ(attr.shm_perm.gid, gid); 189 EXPECT_EQ(attr.shm_perm.cuid, uid); 190 EXPECT_EQ(attr.shm_perm.cgid, gid); 191 EXPECT_EQ(attr.shm_perm.mode, 0777); 192 193 EXPECT_EQ(attr.shm_segsz, kAllocSize); 194 195 EXPECT_EQ(attr.shm_atime, 0); 196 EXPECT_EQ(attr.shm_dtime, 0); 197 198 // Change time is set on creation. 199 EXPECT_GE(attr.shm_ctime, start); 200 201 EXPECT_EQ(attr.shm_cpid, pid); 202 EXPECT_EQ(attr.shm_lpid, 0); 203 204 EXPECT_EQ(attr.shm_nattch, 0); 205 206 // The timestamps only have a resolution of seconds; slow down so we actually 207 // see the timestamps change. 208 absl::SleepFor(absl::Seconds(1)); 209 const time_t pre_attach = time(nullptr); 210 211 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 212 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); 213 214 EXPECT_GE(attr.shm_atime, pre_attach); 215 EXPECT_EQ(attr.shm_dtime, 0); 216 EXPECT_LT(attr.shm_ctime, pre_attach); 217 EXPECT_EQ(attr.shm_lpid, pid); 218 EXPECT_EQ(attr.shm_nattch, 1); 219 220 absl::SleepFor(absl::Seconds(1)); 221 const time_t pre_detach = time(nullptr); 222 223 ASSERT_NO_ERRNO(Shmdt(addr)); 224 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); 225 226 EXPECT_LT(attr.shm_atime, pre_detach); 227 EXPECT_GE(attr.shm_dtime, pre_detach); 228 EXPECT_LT(attr.shm_ctime, pre_detach); 229 EXPECT_EQ(attr.shm_lpid, pid); 230 EXPECT_EQ(attr.shm_nattch, 0); 231 } 232 233 TEST(ShmTest, ShmStat) { 234 // This test relies on the segment we create to be the first one on the 235 // system, causing it to occupy slot 1. We can't reasonably expect this on a 236 // general Linux host. 237 SKIP_IF(!IsRunningOnGvisor()); 238 239 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 240 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 241 struct shmid_ds attr; 242 ASSERT_NO_ERRNO(Shmctl(1, SHM_STAT, &attr)); 243 // This does the same thing as IPC_STAT, so only test that the syscall 244 // succeeds here. 245 } 246 247 TEST(ShmTest, IpcInfo) { 248 struct shminfo info; 249 ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); 250 251 EXPECT_EQ(info.shmmin, 1); // This is always 1, according to the man page. 252 EXPECT_GT(info.shmmax, info.shmmin); 253 EXPECT_GT(info.shmmni, 0); 254 EXPECT_GT(info.shmseg, 0); 255 EXPECT_GT(info.shmall, 0); 256 } 257 258 TEST(ShmTest, ShmInfo) { 259 // Take a snapshot of the system before the test runs. 260 struct shm_info snap; 261 ASSERT_NO_ERRNO(Shmctl(0, SHM_INFO, &snap)); 262 263 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 264 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 265 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 266 267 struct shm_info info; 268 ASSERT_NO_ERRNO(Shmctl(1, SHM_INFO, &info)); 269 270 // We generally can't know what other processes on a linux machine do with 271 // shared memory segments, so we can't test specific numbers on Linux. When 272 // running under gvisor, we're guaranteed to be the only ones using shm, so 273 // we can easily verify machine-wide numbers. 274 if (IsRunningOnGvisor()) { 275 ASSERT_NO_ERRNO(Shmctl(shm.id(), SHM_INFO, &info)); 276 EXPECT_EQ(info.used_ids, snap.used_ids + 1); 277 EXPECT_EQ(info.shm_tot, snap.shm_tot + (kAllocSize / kPageSize)); 278 EXPECT_EQ(info.shm_rss, snap.shm_rss + (kAllocSize / kPageSize)); 279 EXPECT_EQ(info.shm_swp, 0); // Gvisor currently never swaps. 280 } 281 282 ASSERT_NO_ERRNO(Shmdt(addr)); 283 } 284 285 TEST(ShmTest, ShmCtlSet) { 286 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 287 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 288 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 289 290 struct shmid_ds attr; 291 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); 292 ASSERT_EQ(attr.shm_perm.mode, 0777); 293 294 attr.shm_perm.mode = 0766; 295 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_SET, &attr)); 296 297 ASSERT_NO_ERRNO(Shmctl(shm.id(), IPC_STAT, &attr)); 298 ASSERT_EQ(attr.shm_perm.mode, 0766); 299 300 ASSERT_NO_ERRNO(Shmdt(addr)); 301 } 302 303 #ifndef SHM_DEST 304 // Not defined in bionic 305 #define SHM_DEST 0x200 306 #endif 307 308 TEST(ShmTest, RemovedSegmentsAreMarkedDeleted) { 309 ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 310 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 311 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 312 const int id = ASSERT_NO_ERRNO_AND_VALUE(shm.Rmid()); 313 struct shmid_ds attr; 314 ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); 315 EXPECT_NE(attr.shm_perm.mode & SHM_DEST, 0); 316 ASSERT_NO_ERRNO(Shmdt(addr)); 317 } 318 319 TEST(ShmTest, RemovedSegmentsAreDestroyed) { 320 ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 321 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 322 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 323 324 const uint64_t alloc_pages = kAllocSize / kPageSize; 325 326 struct shm_info info; 327 ASSERT_NO_ERRNO(Shmctl(0 /*ignored*/, SHM_INFO, &info)); 328 const uint64_t before = info.shm_tot; 329 330 ASSERT_NO_ERRNO(shm.Rmid()); 331 ASSERT_NO_ERRNO(Shmdt(addr)); 332 333 ASSERT_NO_ERRNO(Shmctl(0 /*ignored*/, SHM_INFO, &info)); 334 if (IsRunningOnGvisor()) { 335 // No guarantees on system-wide shm memory usage on a generic linux host. 336 const uint64_t after = info.shm_tot; 337 EXPECT_EQ(after, before - alloc_pages); 338 } 339 } 340 341 TEST(ShmTest, AllowsAttachToRemovedSegmentWithRefs) { 342 ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 343 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 344 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 345 const int id = ASSERT_NO_ERRNO_AND_VALUE(shm.Rmid()); 346 const char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); 347 ASSERT_NO_ERRNO(Shmdt(addr)); 348 ASSERT_NO_ERRNO(Shmdt(addr2)); 349 } 350 351 TEST(ShmTest, RemovedSegmentsAreNotDiscoverable) { 352 const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 353 const key_t key = ftok(keyfile.path().c_str(), 1); 354 ShmSegment shm = 355 ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); 356 ASSERT_NO_ERRNO(shm.Rmid()); 357 EXPECT_THAT(Shmget(key, kAllocSize, 0777), PosixErrorIs(ENOENT, _)); 358 } 359 360 TEST(ShmDeathTest, ReadonlySegment) { 361 SetupGvisorDeathTest(); 362 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 363 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 364 char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, SHM_RDONLY)); 365 // Reading succeeds. 366 static_cast<void>(addr[0]); 367 // Writing fails. 368 EXPECT_EXIT(addr[0] = 'x', ::testing::KilledBySignal(SIGSEGV), ""); 369 } 370 371 TEST(ShmDeathTest, SegmentNotAccessibleAfterDetach) { 372 // This test is susceptible to races with concurrent mmaps running in parallel 373 // gtest threads since the test relies on the address freed during a shm 374 // segment destruction to remain unused. We run the test body in a forked 375 // child to guarantee a single-threaded context to avoid this. 376 377 SetupGvisorDeathTest(); 378 379 const auto rest = [&] { 380 ShmSegment shm = TEST_CHECK_NO_ERRNO_AND_VALUE( 381 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 382 char* addr = TEST_CHECK_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 383 384 // Mark the segment as destroyed so it's automatically cleaned up when we 385 // crash below. We can't rely on the standard cleanup since the destructor 386 // will not run after the SIGSEGV. Note that this doesn't destroy the 387 // segment immediately since we're still attached to it. 388 TEST_CHECK_NO_ERRNO(shm.Rmid()); 389 390 addr[0] = 'x'; 391 TEST_CHECK_NO_ERRNO(Shmdt(addr)); 392 393 // This access should cause a SIGSEGV. 394 addr[0] = 'x'; 395 }; 396 397 EXPECT_THAT(InForkedProcess(rest), 398 IsPosixErrorOkAndHolds(AnyOf(Eq(W_EXITCODE(0, SIGSEGV)), 399 Eq(W_EXITCODE(0, 128 + SIGSEGV))))); 400 } 401 402 TEST(ShmTest, RequestingSegmentSmallerThanSHMMINFails) { 403 struct shminfo info; 404 ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); 405 const uint64_t size = info.shmmin - 1; 406 EXPECT_THAT(Shmget(IPC_PRIVATE, size, IPC_CREAT | 0777), 407 PosixErrorIs(EINVAL, _)); 408 } 409 410 TEST(ShmTest, RequestingSegmentLargerThanSHMMAXFails) { 411 struct shminfo info; 412 ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); 413 const uint64_t size = info.shmmax + kPageSize; 414 EXPECT_THAT(Shmget(IPC_PRIVATE, size, IPC_CREAT | 0777), 415 PosixErrorIs(EINVAL, _)); 416 } 417 418 TEST(ShmTest, RequestingUnalignedSizeSucceeds) { 419 EXPECT_NO_ERRNO(Shmget(IPC_PRIVATE, 4097, IPC_CREAT | 0777)); 420 } 421 422 TEST(ShmTest, RequestingDuplicateCreationFails) { 423 const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 424 const key_t key = ftok(keyfile.path().c_str(), 1); 425 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 426 Shmget(key, kAllocSize, IPC_CREAT | IPC_EXCL | 0777)); 427 EXPECT_THAT(Shmget(key, kAllocSize, IPC_CREAT | IPC_EXCL | 0777), 428 PosixErrorIs(EEXIST, _)); 429 } 430 431 TEST(ShmTest, NonExistentSegmentsAreNotFound) { 432 const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 433 const key_t key = ftok(keyfile.path().c_str(), 1); 434 // Do not request creation. 435 EXPECT_THAT(Shmget(key, kAllocSize, 0777), PosixErrorIs(ENOENT, _)); 436 } 437 438 TEST(ShmTest, SegmentsSizeFixedOnCreation) { 439 const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 440 const key_t key = ftok(keyfile.path().c_str(), 1); 441 442 // Base segment. 443 const ShmSegment shm = 444 ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); 445 446 // Ask for the same segment at half size. This succeeds. 447 const int id2 = 448 ASSERT_NO_ERRNO_AND_VALUE(ShmgetRaw(key, kAllocSize / 2, 0777)); 449 450 // Ask for the same segment at double size. 451 EXPECT_THAT(Shmget(key, kAllocSize * 2, 0777), PosixErrorIs(EINVAL, _)); 452 453 char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 454 char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id2, nullptr, 0)); 455 456 // We have 2 different maps... 457 EXPECT_NE(addr, addr2); 458 459 // ... And both maps are kAllocSize bytes; despite asking for a half-sized 460 // segment for the second map. 461 addr[kAllocSize - 1] = 'x'; 462 addr2[kAllocSize - 1] = 'x'; 463 464 ASSERT_NO_ERRNO(Shmdt(addr)); 465 ASSERT_NO_ERRNO(Shmdt(addr2)); 466 } 467 468 TEST(ShmTest, PartialUnmap) { 469 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 470 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 471 char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 472 EXPECT_THAT(munmap(addr + (kAllocSize / 4), kAllocSize / 2), 473 SyscallSucceeds()); 474 ASSERT_NO_ERRNO(Shmdt(addr)); 475 } 476 477 // Check that sentry does not panic when asked for a zero-length private shm 478 // segment. Regression test for b/110694797. 479 TEST(ShmTest, GracefullyFailOnZeroLenSegmentCreation) { 480 EXPECT_THAT(Shmget(IPC_PRIVATE, 0, 0), PosixErrorIs(EINVAL, _)); 481 } 482 483 TEST(ShmTest, NoDestructionOfAttachedSegmentWithMultipleRmid) { 484 ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 485 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 486 char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 487 char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 488 489 // There should be 2 refs to the segment from the 2 attachments, and a single 490 // self-reference. Mark the segment as destroyed more than 3 times through 491 // shmctl(RMID). If there's a bug with the ref counting, this should cause the 492 // count to drop to zero. 493 int id = shm.release(); 494 for (int i = 0; i < 6; ++i) { 495 ASSERT_NO_ERRNO(Shmctl<void>(id, IPC_RMID, nullptr)); 496 } 497 498 // Segment should remain accessible. 499 addr[0] = 'x'; 500 ASSERT_NO_ERRNO(Shmdt(addr)); 501 502 // Segment should remain accessible even after one of the two attachments are 503 // detached. 504 addr2[0] = 'x'; 505 ASSERT_NO_ERRNO(Shmdt(addr2)); 506 } 507 508 } // namespace 509 } // namespace testing 510 } // namespace gvisor