diff options
-rw-r--r-- | pkg/sentry/kernel/semaphore/semaphore.go | 18 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/linux64.go | 4 | ||||
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_sem.go | 30 | ||||
-rw-r--r-- | test/syscalls/linux/semaphore.cc | 62 |
4 files changed, 84 insertions, 30 deletions
diff --git a/pkg/sentry/kernel/semaphore/semaphore.go b/pkg/sentry/kernel/semaphore/semaphore.go index db01e4a97..fe2ab1662 100644 --- a/pkg/sentry/kernel/semaphore/semaphore.go +++ b/pkg/sentry/kernel/semaphore/semaphore.go @@ -381,15 +381,24 @@ func (s *Set) Change(ctx context.Context, creds *auth.Credentials, owner fs.File // GetStat extracts semid_ds information from the set. func (s *Set) GetStat(creds *auth.Credentials) (*linux.SemidDS, error) { + // "The calling process must have read permission on the semaphore set." + return s.semStat(creds, fs.PermMask{Read: true}) +} + +// GetStatAny extracts semid_ds information from the set without requiring read access. +func (s *Set) GetStatAny(creds *auth.Credentials) (*linux.SemidDS, error) { + return s.semStat(creds, fs.PermMask{}) +} + +func (s *Set) semStat(creds *auth.Credentials, permMask fs.PermMask) (*linux.SemidDS, error) { s.mu.Lock() defer s.mu.Unlock() - // "The calling process must have read permission on the semaphore set." - if !s.checkPerms(creds, fs.PermMask{Read: true}) { + if !s.checkPerms(creds, permMask) { return nil, syserror.EACCES } - ds := &linux.SemidDS{ + return &linux.SemidDS{ SemPerm: linux.IPCPerm{ Key: uint32(s.key), UID: uint32(creds.UserNamespace.MapFromKUID(s.owner.UID)), @@ -402,8 +411,7 @@ func (s *Set) GetStat(creds *auth.Credentials) (*linux.SemidDS, error) { SemOTime: s.opTime.TimeT(), SemCTime: s.changeTime.TimeT(), SemNSems: uint64(s.Size()), - } - return ds, nil + }, nil } // SetVal overrides a semaphore value, waking up waiters as needed. diff --git a/pkg/sentry/syscalls/linux/linux64.go b/pkg/sentry/syscalls/linux/linux64.go index 4747117b8..ac53a0c0e 100644 --- a/pkg/sentry/syscalls/linux/linux64.go +++ b/pkg/sentry/syscalls/linux/linux64.go @@ -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_STAT_ANY not supported.", nil), + 66: syscalls.Supported("semctl", Semctl), 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_STAT_ANY not supported.", nil), + 191: syscalls.Supported("semctl", Semctl), 192: syscalls.Supported("semtimedop", Semtimedop), 193: syscalls.PartiallySupported("semop", Semop, "Option SEM_UNDO not supported.", nil), 194: syscalls.PartiallySupported("shmget", Shmget, "Option SHM_HUGETLB is not supported.", nil), diff --git a/pkg/sentry/syscalls/linux/sys_sem.go b/pkg/sentry/syscalls/linux/sys_sem.go index 55287f147..f0570d927 100644 --- a/pkg/sentry/syscalls/linux/sys_sem.go +++ b/pkg/sentry/syscalls/linux/sys_sem.go @@ -220,8 +220,16 @@ func Semctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal return uintptr(semid), nil, err case linux.SEM_STAT_ANY: - t.Kernel().EmitUnimplementedEvent(t) - fallthrough + arg := args[3].Pointer() + // id is an index in SEM_STAT. + semid, ds, err := semStatAny(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 default: return 0, nil, syserror.EINVAL @@ -272,7 +280,23 @@ func semStat(t *kernel.Task, index int32) (int32, *linux.SemidDS, error) { } creds := auth.CredentialsFromContext(t) ds, err := set.GetStat(creds) - return set.ID, ds, err + if err != nil { + return 0, ds, err + } + return set.ID, ds, nil +} + +func semStatAny(t *kernel.Task, index int32) (int32, *linux.SemidDS, error) { + set := t.IPCNamespace().SemaphoreRegistry().FindByIndex(index) + if set == nil { + return 0, nil, syserror.EINVAL + } + creds := auth.CredentialsFromContext(t) + ds, err := set.GetStatAny(creds) + if err != nil { + return 0, ds, err + } + return set.ID, ds, nil } func setVal(t *kernel.Task, id int32, num int32, val int16) error { diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc index b8f6ced30..28f51a3bf 100644 --- a/test/syscalls/linux/semaphore.cc +++ b/test/syscalls/linux/semaphore.cc @@ -68,6 +68,15 @@ class AutoSem { int id_ = -1; }; +bool operator==(struct semid_ds const& a, struct semid_ds const& b) { + return a.sem_perm.__key == b.sem_perm.__key && + a.sem_perm.uid == b.sem_perm.uid && a.sem_perm.gid == b.sem_perm.gid && + a.sem_perm.cuid == b.sem_perm.cuid && + a.sem_perm.cgid == b.sem_perm.cgid && + a.sem_perm.mode == b.sem_perm.mode && a.sem_otime == b.sem_otime && + a.sem_ctime == b.sem_ctime && a.sem_nsems == b.sem_nsems; +} + TEST(SemaphoreTest, SemGet) { // Test creation and lookup. AutoSem sem(semget(1, 10, IPC_CREAT)); @@ -843,6 +852,10 @@ TEST(SemaphoreTest, SemopGetncntOnSignal_NoRandomSave) { EXPECT_EQ(semctl(sem.get(), 0, GETNCNT), 0); } +#ifndef SEM_STAT_ANY +#define SEM_STAT_ANY 20 +#endif // SEM_STAT_ANY + TEST(SemaphoreTest, IpcInfo) { constexpr int kLoops = 5; std::set<int> sem_ids; @@ -868,15 +881,7 @@ TEST(SemaphoreTest, IpcInfo) { if (sem_ids.find(sem_id) != sem_ids.end()) { 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); + EXPECT_TRUE(ds == ipc_stat_ds); // Remove the semaphore set's read permission. struct semid_ds ipc_set_ds; @@ -884,9 +889,21 @@ TEST(SemaphoreTest, IpcInfo) { 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; + // IPC_SET command here updates sem_ctime member of the sem. ASSERT_THAT(semctl(sem_id, 0, IPC_SET, &ipc_set_ds), SyscallSucceeds()); ASSERT_THAT(semctl(i, 0, SEM_STAT, &ds), SyscallFailsWithErrno(EACCES)); - + int val = semctl(i, 0, SEM_STAT_ANY, &ds); + if (val == -1) { + // Only if the kernel doesn't support the command SEM_STAT_ANY. + EXPECT_TRUE(errno == EINVAL || errno == EFAULT); + } else { + EXPECT_EQ(sem_id, val); + EXPECT_LE(ipc_stat_ds.sem_ctime, ds.sem_ctime); + ipc_stat_ds.sem_ctime = 0; + ipc_stat_ds.sem_perm.mode = 0200; + ds.sem_ctime = 0; + EXPECT_TRUE(ipc_stat_ds == ds); + } sem_ids_before_max_index.insert(sem_id); } } @@ -946,15 +963,7 @@ TEST(SemaphoreTest, SemInfo) { if (sem_ids.find(sem_id) != sem_ids.end()) { 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); + EXPECT_TRUE(ds == ipc_stat_ds); // Remove the semaphore set's read permission. struct semid_ds ipc_set_ds; @@ -962,9 +971,22 @@ TEST(SemaphoreTest, SemInfo) { 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; + // IPC_SET command here updates sem_ctime member of the sem. ASSERT_THAT(semctl(sem_id, 0, IPC_SET, &ipc_set_ds), SyscallSucceeds()); ASSERT_THAT(semctl(i, 0, SEM_STAT, &ds), SyscallFailsWithErrno(EACCES)); - + int val = semctl(i, 0, SEM_STAT_ANY, &ds); + + if (val == -1) { + // Only if the kernel doesn't support the command SEM_STAT_ANY. + EXPECT_TRUE(errno == EINVAL || errno == EFAULT); + } else { + EXPECT_EQ(val, sem_id); + EXPECT_LE(ipc_stat_ds.sem_ctime, ds.sem_ctime); + ipc_stat_ds.sem_ctime = 0; + ipc_stat_ds.sem_perm.mode = 0200; + ds.sem_ctime = 0; + EXPECT_TRUE(ipc_stat_ds == ds); + } sem_ids_before_max_index.insert(sem_id); } } |