diff options
author | Jing Chen <chjing@google.com> | 2020-12-11 04:19:58 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2020-12-11 04:22:04 -0800 |
commit | 73eccab91ec58aafd1ffdf038993b78a812ba30f (patch) | |
tree | 67d1a83d0c35fd264c2e2ce64466bc220959303a | |
parent | 0eb4acad37c448a298c77090b3475ce6903ecf18 (diff) |
Make semctl IPC_INFO cmd return the index of highest used entry.
PiperOrigin-RevId: 346973338
-rw-r--r-- | pkg/sentry/kernel/semaphore/semaphore.go | 53 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_sem.go | 7 | ||||
-rw-r--r-- | test/syscalls/linux/semaphore.cc | 27 |
3 files changed, 83 insertions, 4 deletions
diff --git a/pkg/sentry/kernel/semaphore/semaphore.go b/pkg/sentry/kernel/semaphore/semaphore.go index 31198d772..3dd3953b3 100644 --- a/pkg/sentry/kernel/semaphore/semaphore.go +++ b/pkg/sentry/kernel/semaphore/semaphore.go @@ -52,6 +52,9 @@ type Registry struct { mu sync.Mutex `state:"nosave"` semaphores map[int32]*Set lastIDUsed int32 + // indexes maintains a mapping between a set's index in virtual array and + // its identifier. + indexes map[int32]int32 } // Set represents a set of semaphores that can be operated atomically. @@ -113,6 +116,7 @@ func NewRegistry(userNS *auth.UserNamespace) *Registry { return &Registry{ userNS: userNS, semaphores: make(map[int32]*Set), + indexes: make(map[int32]int32), } } @@ -163,6 +167,9 @@ func (r *Registry) FindOrCreate(ctx context.Context, key, nsems int32, mode linu } // Apply system limits. + // + // Map semaphores and map indexes in a registry are of the same size, + // check map semaphores only here for the system limit. if len(r.semaphores) >= setsMax { return nil, syserror.EINVAL } @@ -192,6 +199,23 @@ func (r *Registry) IPCInfo() *linux.SemInfo { } } +// HighestIndex returns the index of the highest used entry in +// the kernel's array. +func (r *Registry) HighestIndex() int32 { + r.mu.Lock() + defer r.mu.Unlock() + + // By default, highest used index is 0 even though + // there is no semaphroe set. + var highestIndex int32 + for index := range r.indexes { + if index > highestIndex { + highestIndex = index + } + } + return highestIndex +} + // RemoveID removes set with give 'id' from the registry and marks the set as // dead. All waiters will be awakened and fail. func (r *Registry) RemoveID(id int32, creds *auth.Credentials) error { @@ -202,6 +226,11 @@ func (r *Registry) RemoveID(id int32, creds *auth.Credentials) error { if set == nil { return syserror.EINVAL } + index, found := r.findIndexByID(id) + if !found { + // Inconsistent state. + panic(fmt.Sprintf("unable to find an index for ID: %d", id)) + } set.mu.Lock() defer set.mu.Unlock() @@ -213,6 +242,7 @@ func (r *Registry) RemoveID(id int32, creds *auth.Credentials) error { } delete(r.semaphores, set.ID) + delete(r.indexes, index) set.destroy() return nil } @@ -236,6 +266,11 @@ func (r *Registry) newSet(ctx context.Context, key int32, owner, creator fs.File continue } if r.semaphores[id] == nil { + index, found := r.findFirstAvailableIndex() + if !found { + panic("unable to find an available index") + } + r.indexes[index] = id r.lastIDUsed = id r.semaphores[id] = set set.ID = id @@ -263,6 +298,24 @@ func (r *Registry) findByKey(key int32) *Set { return nil } +func (r *Registry) findIndexByID(id int32) (int32, bool) { + for k, v := range r.indexes { + if v == id { + return k, true + } + } + return 0, false +} + +func (r *Registry) findFirstAvailableIndex() (int32, bool) { + for index := int32(0); index < setsMax; index++ { + if _, present := r.indexes[index]; !present { + return index, true + } + } + return 0, false +} + func (r *Registry) totalSems() int { totalSems := 0 for _, v := range r.semaphores { diff --git a/pkg/sentry/syscalls/linux/sys_sem.go b/pkg/sentry/syscalls/linux/sys_sem.go index a1601676f..a62a6b3b5 100644 --- a/pkg/sentry/syscalls/linux/sys_sem.go +++ b/pkg/sentry/syscalls/linux/sys_sem.go @@ -150,9 +150,10 @@ func Semctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal buf := args[3].Pointer() r := t.IPCNamespace().SemaphoreRegistry() info := r.IPCInfo() - _, err := info.CopyOut(t, buf) - // TODO(gvisor.dev/issue/137): Return the index of the highest used entry. - return 0, nil, err + if _, err := info.CopyOut(t, buf); err != nil { + return 0, nil, err + } + return uintptr(r.HighestIndex()), nil, nil case linux.SEM_INFO, linux.SEM_STAT, diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc index 1c1bf6a57..fb4695e72 100644 --- a/test/syscalls/linux/semaphore.cc +++ b/test/syscalls/linux/semaphore.cc @@ -20,6 +20,7 @@ #include <atomic> #include <cerrno> #include <ctime> +#include <stack> #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -774,8 +775,32 @@ TEST(SemaphoreTest, SemopGetncntOnSignal_NoRandomSave) { } TEST(SemaphoreTest, IpcInfo) { + std::stack<int> sem_ids; + std::stack<int> max_used_indexes; struct seminfo info; - ASSERT_THAT(semctl(0, 0, IPC_INFO, &info), SyscallSucceeds()); + 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(); + } + ASSERT_THAT(semctl(0, 0, IPC_INFO, &info), SyscallSucceedsWithValue(0)); EXPECT_EQ(info.semmap, 1024000000); EXPECT_EQ(info.semmni, 32000); |