From 1e56a2f9a29ff72eada493bf024a4e3fd5a963b6 Mon Sep 17 00:00:00 2001 From: Jing Chen Date: Tue, 15 Dec 2020 16:03:41 -0800 Subject: Implement command SEM_INFO and SEM_STAT for semctl. PiperOrigin-RevId: 347711998 --- test/syscalls/linux/semaphore.cc | 182 ++++++++++++++++++++++++++++++++------- 1 file changed, 152 insertions(+), 30 deletions(-) (limited to 'test') diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc index fb4695e72..c2f080917 100644 --- a/test/syscalls/linux/semaphore.cc +++ b/test/syscalls/linux/semaphore.cc @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -32,10 +32,23 @@ #include "test/util/test_util.h" #include "test/util/thread_util.h" +using ::testing::Contains; + namespace gvisor { namespace testing { namespace { +constexpr int kSemMap = 1024000000; +constexpr int kSemMni = 32000; +constexpr int kSemMns = 1024000000; +constexpr int kSemMnu = 1024000000; +constexpr int kSemMsl = 32000; +constexpr int kSemOpm = 500; +constexpr int kSemUme = 500; +constexpr int kSemUsz = 20; +constexpr int kSemVmx = 32767; +constexpr int kSemAem = 32767; + class AutoSem { public: explicit AutoSem(int id) : id_(id) {} @@ -775,42 +788,151 @@ TEST(SemaphoreTest, SemopGetncntOnSignal_NoRandomSave) { } TEST(SemaphoreTest, IpcInfo) { - std::stack sem_ids; - std::stack max_used_indexes; + constexpr int kLoops = 5; + std::set sem_ids; struct seminfo info; - for (int i = 0; i < 3; i++) { - int sem_id = 0; - ASSERT_THAT(sem_id = semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT), - SyscallSucceeds()); - sem_ids.push(sem_id); - int max_used_index = 0; - EXPECT_THAT(max_used_index = semctl(0, 0, IPC_INFO, &info), - SyscallSucceeds()); - if (!max_used_indexes.empty()) { - EXPECT_GT(max_used_index, max_used_indexes.top()); + // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); + ASSERT_THAT(semctl(0, 0, IPC_INFO, &info), SyscallSucceedsWithValue(0)); + for (int i = 0; i < kLoops; i++) { + AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + sem_ids.insert(sem.release()); + } + ASSERT_EQ(sem_ids.size(), kLoops); + + int max_used_index = 0; + EXPECT_THAT(max_used_index = semctl(0, 0, IPC_INFO, &info), + SyscallSucceeds()); + + int index_count = 0; + for (int i = 0; i <= max_used_index; i++) { + struct semid_ds ds = {}; + int sem_id = semctl(i, 0, SEM_STAT, &ds); + // Only if index i is used within the registry. + if (sem_id != -1) { + ASSERT_THAT(sem_ids, Contains(sem_id)); + struct semid_ds ipc_stat_ds; + ASSERT_THAT(semctl(sem_id, 0, IPC_STAT, &ipc_stat_ds), SyscallSucceeds()); + EXPECT_EQ(ds.sem_perm.__key, ipc_stat_ds.sem_perm.__key); + EXPECT_EQ(ds.sem_perm.uid, ipc_stat_ds.sem_perm.uid); + EXPECT_EQ(ds.sem_perm.gid, ipc_stat_ds.sem_perm.gid); + EXPECT_EQ(ds.sem_perm.cuid, ipc_stat_ds.sem_perm.cuid); + EXPECT_EQ(ds.sem_perm.cgid, ipc_stat_ds.sem_perm.cgid); + EXPECT_EQ(ds.sem_perm.mode, ipc_stat_ds.sem_perm.mode); + EXPECT_EQ(ds.sem_otime, ipc_stat_ds.sem_otime); + EXPECT_EQ(ds.sem_ctime, ipc_stat_ds.sem_ctime); + EXPECT_EQ(ds.sem_nsems, ipc_stat_ds.sem_nsems); + + // Remove the semaphore set's read permission. + struct semid_ds ipc_set_ds; + ipc_set_ds.sem_perm.uid = getuid(); + ipc_set_ds.sem_perm.gid = getgid(); + // Keep the semaphore set's write permission so that it could be removed. + ipc_set_ds.sem_perm.mode = 0200; + ASSERT_THAT(semctl(sem_id, 0, IPC_SET, &ipc_set_ds), SyscallSucceeds()); + ASSERT_THAT(semctl(i, 0, SEM_STAT, &ds), SyscallFailsWithErrno(EACCES)); + + index_count += 1; } - max_used_indexes.push(max_used_index); } - while (!sem_ids.empty()) { - int sem_id = sem_ids.top(); - sem_ids.pop(); + EXPECT_EQ(index_count, kLoops); + ASSERT_THAT(semctl(0, 0, IPC_INFO, &info), + SyscallSucceedsWithValue(max_used_index)); + for (const int sem_id : sem_ids) { ASSERT_THAT(semctl(sem_id, 0, IPC_RMID), SyscallSucceeds()); - int max_index = max_used_indexes.top(); - EXPECT_THAT(max_index = semctl(0, 0, IPC_INFO, &info), SyscallSucceeds()); - EXPECT_GE(max_used_indexes.top(), max_index); - max_used_indexes.pop(); } + + ASSERT_THAT(semctl(0, 0, IPC_INFO, &info), SyscallSucceedsWithValue(0)); + EXPECT_EQ(info.semmap, kSemMap); + EXPECT_EQ(info.semmni, kSemMni); + EXPECT_EQ(info.semmns, kSemMns); + EXPECT_EQ(info.semmnu, kSemMnu); + EXPECT_EQ(info.semmsl, kSemMsl); + EXPECT_EQ(info.semopm, kSemOpm); + EXPECT_EQ(info.semume, kSemUme); + EXPECT_EQ(info.semusz, kSemUsz); + EXPECT_EQ(info.semvmx, kSemVmx); + EXPECT_EQ(info.semaem, kSemAem); +} + +TEST(SemaphoreTest, SemInfo) { + constexpr int kLoops = 5; + constexpr int kSemSetSize = 3; + std::set sem_ids; + struct seminfo info; + // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); ASSERT_THAT(semctl(0, 0, IPC_INFO, &info), SyscallSucceedsWithValue(0)); + for (int i = 0; i < kLoops; i++) { + AutoSem sem(semget(IPC_PRIVATE, kSemSetSize, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + sem_ids.insert(sem.release()); + } + ASSERT_EQ(sem_ids.size(), kLoops); + int max_used_index = 0; + EXPECT_THAT(max_used_index = semctl(0, 0, SEM_INFO, &info), + SyscallSucceeds()); + EXPECT_EQ(info.semmap, kSemMap); + EXPECT_EQ(info.semmni, kSemMni); + EXPECT_EQ(info.semmns, kSemMns); + EXPECT_EQ(info.semmnu, kSemMnu); + EXPECT_EQ(info.semmsl, kSemMsl); + EXPECT_EQ(info.semopm, kSemOpm); + EXPECT_EQ(info.semume, kSemUme); + EXPECT_EQ(info.semusz, sem_ids.size()); + EXPECT_EQ(info.semvmx, kSemVmx); + EXPECT_EQ(info.semaem, sem_ids.size() * kSemSetSize); + + int index_count = 0; + for (int i = 0; i <= max_used_index; i++) { + struct semid_ds ds = {}; + int sem_id = semctl(i, 0, SEM_STAT, &ds); + // Only if index i is used within the registry. + if (sem_id != -1) { + ASSERT_THAT(sem_ids, Contains(sem_id)); + struct semid_ds ipc_stat_ds; + ASSERT_THAT(semctl(sem_id, 0, IPC_STAT, &ipc_stat_ds), SyscallSucceeds()); + EXPECT_EQ(ds.sem_perm.__key, ipc_stat_ds.sem_perm.__key); + EXPECT_EQ(ds.sem_perm.uid, ipc_stat_ds.sem_perm.uid); + EXPECT_EQ(ds.sem_perm.gid, ipc_stat_ds.sem_perm.gid); + EXPECT_EQ(ds.sem_perm.cuid, ipc_stat_ds.sem_perm.cuid); + EXPECT_EQ(ds.sem_perm.cgid, ipc_stat_ds.sem_perm.cgid); + EXPECT_EQ(ds.sem_perm.mode, ipc_stat_ds.sem_perm.mode); + EXPECT_EQ(ds.sem_otime, ipc_stat_ds.sem_otime); + EXPECT_EQ(ds.sem_ctime, ipc_stat_ds.sem_ctime); + EXPECT_EQ(ds.sem_nsems, ipc_stat_ds.sem_nsems); + + // Remove the semaphore set's read permission. + struct semid_ds ipc_set_ds; + ipc_set_ds.sem_perm.uid = getuid(); + ipc_set_ds.sem_perm.gid = getgid(); + // Keep the semaphore set's write permission so that it could be removed. + ipc_set_ds.sem_perm.mode = 0200; + ASSERT_THAT(semctl(sem_id, 0, IPC_SET, &ipc_set_ds), SyscallSucceeds()); + ASSERT_THAT(semctl(i, 0, SEM_STAT, &ds), SyscallFailsWithErrno(EACCES)); + + index_count += 1; + } + } + EXPECT_EQ(index_count, kLoops); + ASSERT_THAT(semctl(0, 0, SEM_INFO, &info), + SyscallSucceedsWithValue(max_used_index)); + for (const int sem_id : sem_ids) { + ASSERT_THAT(semctl(sem_id, 0, IPC_RMID), SyscallSucceeds()); + } - EXPECT_EQ(info.semmap, 1024000000); - EXPECT_EQ(info.semmni, 32000); - EXPECT_EQ(info.semmns, 1024000000); - EXPECT_EQ(info.semmnu, 1024000000); - EXPECT_EQ(info.semmsl, 32000); - EXPECT_EQ(info.semopm, 500); - EXPECT_EQ(info.semume, 500); - EXPECT_EQ(info.semvmx, 32767); - EXPECT_EQ(info.semaem, 32767); + ASSERT_THAT(semctl(0, 0, SEM_INFO, &info), SyscallSucceedsWithValue(0)); + EXPECT_EQ(info.semmap, kSemMap); + EXPECT_EQ(info.semmni, kSemMni); + EXPECT_EQ(info.semmns, kSemMns); + EXPECT_EQ(info.semmnu, kSemMnu); + EXPECT_EQ(info.semmsl, kSemMsl); + EXPECT_EQ(info.semopm, kSemOpm); + EXPECT_EQ(info.semume, kSemUme); + EXPECT_EQ(info.semusz, 0); + EXPECT_EQ(info.semvmx, kSemVmx); + EXPECT_EQ(info.semaem, 0); } } // namespace -- cgit v1.2.3