summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJing Chen <chjing@google.com>2020-12-11 04:19:58 -0800
committergVisor bot <gvisor-bot@google.com>2020-12-11 04:22:04 -0800
commit73eccab91ec58aafd1ffdf038993b78a812ba30f (patch)
tree67d1a83d0c35fd264c2e2ce64466bc220959303a
parent0eb4acad37c448a298c77090b3475ce6903ecf18 (diff)
Make semctl IPC_INFO cmd return the index of highest used entry.
PiperOrigin-RevId: 346973338
-rw-r--r--pkg/sentry/kernel/semaphore/semaphore.go53
-rw-r--r--pkg/sentry/syscalls/linux/sys_sem.go7
-rw-r--r--test/syscalls/linux/semaphore.cc27
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);