mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement command SEM_INFO and SEM_STAT for semctl.
PiperOrigin-RevId: 347711998
This commit is contained in:
@@ -43,10 +43,10 @@ const (
|
||||
SEMVMX = 32767
|
||||
SEMAEM = SEMVMX
|
||||
|
||||
// followings are unused in kernel
|
||||
SEMUME = SEMOPM
|
||||
SEMMNU = SEMMNS
|
||||
SEMMAP = SEMMNS
|
||||
SEMUSZ = 20
|
||||
)
|
||||
|
||||
const SEM_UNDO = 0x1000
|
||||
|
||||
@@ -193,12 +193,26 @@ func (r *Registry) IPCInfo() *linux.SemInfo {
|
||||
SemMsl: linux.SEMMSL,
|
||||
SemOpm: linux.SEMOPM,
|
||||
SemUme: linux.SEMUME,
|
||||
SemUsz: 0, // SemUsz not supported.
|
||||
SemUsz: linux.SEMUSZ,
|
||||
SemVmx: linux.SEMVMX,
|
||||
SemAem: linux.SEMAEM,
|
||||
}
|
||||
}
|
||||
|
||||
// SemInfo returns a seminfo structure containing the same information as
|
||||
// for IPC_INFO, except that SemUsz field returns the number of existing
|
||||
// semaphore sets, and SemAem field returns the number of existing semaphores.
|
||||
func (r *Registry) SemInfo() *linux.SemInfo {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
info := r.IPCInfo()
|
||||
info.SemUsz = uint32(len(r.semaphores))
|
||||
info.SemAem = uint32(r.totalSems())
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
// HighestIndex returns the index of the highest used entry in
|
||||
// the kernel's array.
|
||||
func (r *Registry) HighestIndex() int32 {
|
||||
@@ -289,6 +303,18 @@ func (r *Registry) FindByID(id int32) *Set {
|
||||
return r.semaphores[id]
|
||||
}
|
||||
|
||||
// FindByIndex looks up a set given an index.
|
||||
func (r *Registry) FindByIndex(index int32) *Set {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
id, present := r.indexes[index]
|
||||
if !present {
|
||||
return nil
|
||||
}
|
||||
return r.semaphores[id]
|
||||
}
|
||||
|
||||
func (r *Registry) findByKey(key int32) *Set {
|
||||
for _, v := range r.semaphores {
|
||||
if v.key == key {
|
||||
|
||||
@@ -118,7 +118,7 @@ var AMD64 = &kernel.SyscallTable{
|
||||
63: syscalls.Supported("uname", Uname),
|
||||
64: syscalls.Supported("semget", Semget),
|
||||
65: syscalls.PartiallySupported("semop", Semop, "Option SEM_UNDO not supported.", nil),
|
||||
66: syscalls.PartiallySupported("semctl", Semctl, "Options SEM_INFO, SEM_STAT, SEM_STAT_ANY not supported.", nil),
|
||||
66: syscalls.PartiallySupported("semctl", Semctl, "Options SEM_STAT_ANY not supported.", nil),
|
||||
67: syscalls.Supported("shmdt", Shmdt),
|
||||
68: syscalls.ErrorWithEvent("msgget", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921)
|
||||
69: syscalls.ErrorWithEvent("msgsnd", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921)
|
||||
@@ -619,7 +619,7 @@ var ARM64 = &kernel.SyscallTable{
|
||||
188: syscalls.ErrorWithEvent("msgrcv", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921)
|
||||
189: syscalls.ErrorWithEvent("msgsnd", syserror.ENOSYS, "", []string{"gvisor.dev/issue/135"}), // TODO(b/29354921)
|
||||
190: syscalls.Supported("semget", Semget),
|
||||
191: syscalls.PartiallySupported("semctl", Semctl, "Options SEM_INFO, SEM_STAT, SEM_STAT_ANY not supported.", nil),
|
||||
191: syscalls.PartiallySupported("semctl", Semctl, "Options SEM_STAT_ANY not supported.", nil),
|
||||
192: syscalls.ErrorWithEvent("semtimedop", syserror.ENOSYS, "", []string{"gvisor.dev/issue/137"}),
|
||||
193: syscalls.PartiallySupported("semop", Semop, "Option SEM_UNDO not supported.", nil),
|
||||
194: syscalls.PartiallySupported("shmget", Shmget, "Option SHM_HUGETLB is not supported.", nil),
|
||||
|
||||
@@ -155,10 +155,28 @@ func Semctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
}
|
||||
return uintptr(r.HighestIndex()), nil, nil
|
||||
|
||||
case linux.SEM_INFO,
|
||||
linux.SEM_STAT,
|
||||
linux.SEM_STAT_ANY:
|
||||
case linux.SEM_INFO:
|
||||
buf := args[3].Pointer()
|
||||
r := t.IPCNamespace().SemaphoreRegistry()
|
||||
info := r.SemInfo()
|
||||
if _, err := info.CopyOut(t, buf); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return uintptr(r.HighestIndex()), nil, nil
|
||||
|
||||
case linux.SEM_STAT:
|
||||
arg := args[3].Pointer()
|
||||
// id is an index in SEM_STAT.
|
||||
semid, ds, err := semStat(t, id)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if _, err := ds.CopyOut(t, arg); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return uintptr(semid), nil, err
|
||||
|
||||
case linux.SEM_STAT_ANY:
|
||||
t.Kernel().EmitUnimplementedEvent(t)
|
||||
fallthrough
|
||||
|
||||
@@ -203,6 +221,17 @@ func ipcStat(t *kernel.Task, id int32) (*linux.SemidDS, error) {
|
||||
return set.GetStat(creds)
|
||||
}
|
||||
|
||||
func semStat(t *kernel.Task, index int32) (int32, *linux.SemidDS, error) {
|
||||
r := t.IPCNamespace().SemaphoreRegistry()
|
||||
set := r.FindByIndex(index)
|
||||
if set == nil {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
ds, err := set.GetStat(creds)
|
||||
return set.ID, ds, err
|
||||
}
|
||||
|
||||
func setVal(t *kernel.Task, id int32, num int32, val int16) error {
|
||||
r := t.IPCNamespace().SemaphoreRegistry()
|
||||
set := r.FindByID(id)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <ctime>
|
||||
#include <stack>
|
||||
#include <set>
|
||||
|
||||
#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<int> sem_ids;
|
||||
std::stack<int> max_used_indexes;
|
||||
constexpr int kLoops = 5;
|
||||
std::set<int> 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());
|
||||
}
|
||||
max_used_indexes.push(max_used_index);
|
||||
}
|
||||
while (!sem_ids.empty()) {
|
||||
int sem_id = sem_ids.top();
|
||||
sem_ids.pop();
|
||||
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();
|
||||
}
|
||||
// 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);
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
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<int> 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());
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user