github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 TEST(ShmTest, RemovedSegmentsAreMarkedDeleted) { 304 ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 305 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 306 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 307 const int id = ASSERT_NO_ERRNO_AND_VALUE(shm.Rmid()); 308 struct shmid_ds attr; 309 ASSERT_NO_ERRNO(Shmctl(id, IPC_STAT, &attr)); 310 EXPECT_NE(attr.shm_perm.mode & SHM_DEST, 0); 311 ASSERT_NO_ERRNO(Shmdt(addr)); 312 } 313 314 TEST(ShmTest, RemovedSegmentsAreDestroyed) { 315 ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 316 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 317 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 318 319 const uint64_t alloc_pages = kAllocSize / kPageSize; 320 321 struct shm_info info; 322 ASSERT_NO_ERRNO(Shmctl(0 /*ignored*/, SHM_INFO, &info)); 323 const uint64_t before = info.shm_tot; 324 325 ASSERT_NO_ERRNO(shm.Rmid()); 326 ASSERT_NO_ERRNO(Shmdt(addr)); 327 328 ASSERT_NO_ERRNO(Shmctl(0 /*ignored*/, SHM_INFO, &info)); 329 if (IsRunningOnGvisor()) { 330 // No guarantees on system-wide shm memory usage on a generic linux host. 331 const uint64_t after = info.shm_tot; 332 EXPECT_EQ(after, before - alloc_pages); 333 } 334 } 335 336 TEST(ShmTest, AllowsAttachToRemovedSegmentWithRefs) { 337 ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 338 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 339 const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 340 const int id = ASSERT_NO_ERRNO_AND_VALUE(shm.Rmid()); 341 const char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id, nullptr, 0)); 342 ASSERT_NO_ERRNO(Shmdt(addr)); 343 ASSERT_NO_ERRNO(Shmdt(addr2)); 344 } 345 346 TEST(ShmTest, RemovedSegmentsAreNotDiscoverable) { 347 const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 348 const key_t key = ftok(keyfile.path().c_str(), 1); 349 ShmSegment shm = 350 ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); 351 ASSERT_NO_ERRNO(shm.Rmid()); 352 EXPECT_THAT(Shmget(key, kAllocSize, 0777), PosixErrorIs(ENOENT, _)); 353 } 354 355 TEST(ShmDeathTest, ReadonlySegment) { 356 SetupGvisorDeathTest(); 357 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 358 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 359 char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, SHM_RDONLY)); 360 // Reading succeeds. 361 static_cast<void>(addr[0]); 362 // Writing fails. 363 EXPECT_EXIT(addr[0] = 'x', ::testing::KilledBySignal(SIGSEGV), ""); 364 } 365 366 TEST(ShmDeathTest, SegmentNotAccessibleAfterDetach) { 367 // This test is susceptible to races with concurrent mmaps running in parallel 368 // gtest threads since the test relies on the address freed during a shm 369 // segment destruction to remain unused. We run the test body in a forked 370 // child to guarantee a single-threaded context to avoid this. 371 372 SetupGvisorDeathTest(); 373 374 const auto rest = [&] { 375 ShmSegment shm = TEST_CHECK_NO_ERRNO_AND_VALUE( 376 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 377 char* addr = TEST_CHECK_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 378 379 // Mark the segment as destroyed so it's automatically cleaned up when we 380 // crash below. We can't rely on the standard cleanup since the destructor 381 // will not run after the SIGSEGV. Note that this doesn't destroy the 382 // segment immediately since we're still attached to it. 383 TEST_CHECK_NO_ERRNO(shm.Rmid()); 384 385 addr[0] = 'x'; 386 TEST_CHECK_NO_ERRNO(Shmdt(addr)); 387 388 // This access should cause a SIGSEGV. 389 addr[0] = 'x'; 390 }; 391 392 EXPECT_THAT(InForkedProcess(rest), 393 IsPosixErrorOkAndHolds(AnyOf(Eq(W_EXITCODE(0, SIGSEGV)), 394 Eq(W_EXITCODE(0, 128 + SIGSEGV))))); 395 } 396 397 TEST(ShmTest, RequestingSegmentSmallerThanSHMMINFails) { 398 struct shminfo info; 399 ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); 400 const uint64_t size = info.shmmin - 1; 401 EXPECT_THAT(Shmget(IPC_PRIVATE, size, IPC_CREAT | 0777), 402 PosixErrorIs(EINVAL, _)); 403 } 404 405 TEST(ShmTest, RequestingSegmentLargerThanSHMMAXFails) { 406 struct shminfo info; 407 ASSERT_NO_ERRNO(Shmctl(0, IPC_INFO, &info)); 408 const uint64_t size = info.shmmax + kPageSize; 409 EXPECT_THAT(Shmget(IPC_PRIVATE, size, IPC_CREAT | 0777), 410 PosixErrorIs(EINVAL, _)); 411 } 412 413 TEST(ShmTest, RequestingUnalignedSizeSucceeds) { 414 EXPECT_NO_ERRNO(Shmget(IPC_PRIVATE, 4097, IPC_CREAT | 0777)); 415 } 416 417 TEST(ShmTest, RequestingDuplicateCreationFails) { 418 const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 419 const key_t key = ftok(keyfile.path().c_str(), 1); 420 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 421 Shmget(key, kAllocSize, IPC_CREAT | IPC_EXCL | 0777)); 422 EXPECT_THAT(Shmget(key, kAllocSize, IPC_CREAT | IPC_EXCL | 0777), 423 PosixErrorIs(EEXIST, _)); 424 } 425 426 TEST(ShmTest, NonExistentSegmentsAreNotFound) { 427 const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 428 const key_t key = ftok(keyfile.path().c_str(), 1); 429 // Do not request creation. 430 EXPECT_THAT(Shmget(key, kAllocSize, 0777), PosixErrorIs(ENOENT, _)); 431 } 432 433 TEST(ShmTest, SegmentsSizeFixedOnCreation) { 434 const TempPath keyfile = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); 435 const key_t key = ftok(keyfile.path().c_str(), 1); 436 437 // Base segment. 438 const ShmSegment shm = 439 ASSERT_NO_ERRNO_AND_VALUE(Shmget(key, kAllocSize, IPC_CREAT | 0777)); 440 441 // Ask for the same segment at half size. This succeeds. 442 const int id2 = 443 ASSERT_NO_ERRNO_AND_VALUE(ShmgetRaw(key, kAllocSize / 2, 0777)); 444 445 // Ask for the same segment at double size. 446 EXPECT_THAT(Shmget(key, kAllocSize * 2, 0777), PosixErrorIs(EINVAL, _)); 447 448 char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 449 char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(id2, nullptr, 0)); 450 451 // We have 2 different maps... 452 EXPECT_NE(addr, addr2); 453 454 // ... And both maps are kAllocSize bytes; despite asking for a half-sized 455 // segment for the second map. 456 addr[kAllocSize - 1] = 'x'; 457 addr2[kAllocSize - 1] = 'x'; 458 459 ASSERT_NO_ERRNO(Shmdt(addr)); 460 ASSERT_NO_ERRNO(Shmdt(addr2)); 461 } 462 463 TEST(ShmTest, PartialUnmap) { 464 const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 465 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 466 char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 467 EXPECT_THAT(munmap(addr + (kAllocSize / 4), kAllocSize / 2), 468 SyscallSucceeds()); 469 ASSERT_NO_ERRNO(Shmdt(addr)); 470 } 471 472 // Check that sentry does not panic when asked for a zero-length private shm 473 // segment. Regression test for b/110694797. 474 TEST(ShmTest, GracefullyFailOnZeroLenSegmentCreation) { 475 EXPECT_THAT(Shmget(IPC_PRIVATE, 0, 0), PosixErrorIs(EINVAL, _)); 476 } 477 478 TEST(ShmTest, NoDestructionOfAttachedSegmentWithMultipleRmid) { 479 ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( 480 Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); 481 char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 482 char* addr2 = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); 483 484 // There should be 2 refs to the segment from the 2 attachments, and a single 485 // self-reference. Mark the segment as destroyed more than 3 times through 486 // shmctl(RMID). If there's a bug with the ref counting, this should cause the 487 // count to drop to zero. 488 int id = shm.release(); 489 for (int i = 0; i < 6; ++i) { 490 ASSERT_NO_ERRNO(Shmctl<void>(id, IPC_RMID, nullptr)); 491 } 492 493 // Segment should remain accessible. 494 addr[0] = 'x'; 495 ASSERT_NO_ERRNO(Shmdt(addr)); 496 497 // Segment should remain accessible even after one of the two attachments are 498 // detached. 499 addr2[0] = 'x'; 500 ASSERT_NO_ERRNO(Shmdt(addr2)); 501 } 502 503 } // namespace 504 } // namespace testing 505 } // namespace gvisor