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