mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement semctl(2) SETALL and GETALL
PiperOrigin-RevId: 232914984 Change-Id: Id2643d7ad8e986ca9be76d860788a71db2674cda
This commit is contained in:
committed by
Shentubot
parent
3c71e025e8
commit
9ef3427ac1
@@ -16,6 +16,7 @@
|
||||
package semaphore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
|
||||
@@ -75,7 +76,10 @@ type Set struct {
|
||||
perms fs.FilePermissions
|
||||
opTime ktime.Time
|
||||
changeTime ktime.Time
|
||||
sems []sem
|
||||
|
||||
// sems holds all semaphores in the set. The slice itself is immutable after
|
||||
// it's been set, however each 'sem' object in the slice requires 'mu' lock.
|
||||
sems []sem
|
||||
|
||||
// dead is set to true when the set is removed and can't be reached anymore.
|
||||
// All waiters must wake up and fail when set is dead.
|
||||
@@ -136,7 +140,7 @@ func (r *Registry) FindOrCreate(ctx context.Context, key, nsems int32, mode linu
|
||||
}
|
||||
|
||||
// Validate parameters.
|
||||
if nsems > int32(set.size()) {
|
||||
if nsems > int32(set.Size()) {
|
||||
return nil, syserror.EINVAL
|
||||
}
|
||||
if create && exclusive {
|
||||
@@ -244,19 +248,20 @@ func (r *Registry) findByKey(key int32) *Set {
|
||||
func (r *Registry) totalSems() int {
|
||||
totalSems := 0
|
||||
for _, v := range r.semaphores {
|
||||
totalSems += v.size()
|
||||
totalSems += v.Size()
|
||||
}
|
||||
return totalSems
|
||||
}
|
||||
|
||||
func (s *Set) findSem(num int32) *sem {
|
||||
if num < 0 || int(num) >= s.size() {
|
||||
if num < 0 || int(num) >= s.Size() {
|
||||
return nil
|
||||
}
|
||||
return &s.sems[num]
|
||||
}
|
||||
|
||||
func (s *Set) size() int {
|
||||
// Size returns the number of semaphores in the set. Size is immutable.
|
||||
func (s *Set) Size() int {
|
||||
return len(s.sems)
|
||||
}
|
||||
|
||||
@@ -303,6 +308,39 @@ func (s *Set) SetVal(ctx context.Context, num int32, val int16, creds *auth.Cred
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetValAll overrides all semaphores values, waking up waiters as needed.
|
||||
//
|
||||
// 'len(vals)' must be equal to 's.Size()'.
|
||||
func (s *Set) SetValAll(ctx context.Context, vals []uint16, creds *auth.Credentials) error {
|
||||
if len(vals) != s.Size() {
|
||||
panic(fmt.Sprintf("vals length (%d) different that Set.Size() (%d)", len(vals), s.Size()))
|
||||
}
|
||||
|
||||
for _, val := range vals {
|
||||
if val < 0 || val > valueMax {
|
||||
return syserror.ERANGE
|
||||
}
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
// "The calling process must have alter permission on the semaphore set."
|
||||
if !s.checkPerms(creds, fs.PermMask{Write: true}) {
|
||||
return syserror.EACCES
|
||||
}
|
||||
|
||||
for i, val := range vals {
|
||||
sem := &s.sems[i]
|
||||
|
||||
// TODO: Clear undo entries in all processes
|
||||
sem.value = int16(val)
|
||||
sem.wakeWaiters()
|
||||
}
|
||||
s.changeTime = ktime.NowFromContext(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetVal returns a semaphore value.
|
||||
func (s *Set) GetVal(num int32, creds *auth.Credentials) (int16, error) {
|
||||
s.mu.Lock()
|
||||
@@ -320,6 +358,23 @@ func (s *Set) GetVal(num int32, creds *auth.Credentials) (int16, error) {
|
||||
return sem.value, nil
|
||||
}
|
||||
|
||||
// GetValAll returns value for all semaphores.
|
||||
func (s *Set) GetValAll(creds *auth.Credentials) ([]uint16, 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}) {
|
||||
return nil, syserror.EACCES
|
||||
}
|
||||
|
||||
vals := make([]uint16, s.Size())
|
||||
for i, sem := range s.sems {
|
||||
vals[i] = uint16(sem.value)
|
||||
}
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// ExecuteOps attempts to execute a list of operations to the set. It only
|
||||
// succeeds when all operations can be applied. No changes are made if it fails.
|
||||
//
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
|
||||
"gvisor.googlesource.com/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
@@ -97,10 +98,18 @@ func Semctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
}
|
||||
return 0, nil, setVal(t, id, num, int16(val))
|
||||
|
||||
case linux.SETALL:
|
||||
array := args[3].Pointer()
|
||||
return 0, nil, setValAll(t, id, array)
|
||||
|
||||
case linux.GETVAL:
|
||||
v, err := getVal(t, id, num)
|
||||
return uintptr(v), nil, err
|
||||
|
||||
case linux.GETALL:
|
||||
array := args[3].Pointer()
|
||||
return 0, nil, getValAll(t, id, array)
|
||||
|
||||
case linux.IPC_RMID:
|
||||
return 0, nil, remove(t, id)
|
||||
|
||||
@@ -155,6 +164,20 @@ func setVal(t *kernel.Task, id int32, num int32, val int16) error {
|
||||
return set.SetVal(t, num, val, creds)
|
||||
}
|
||||
|
||||
func setValAll(t *kernel.Task, id int32, array usermem.Addr) error {
|
||||
r := t.IPCNamespace().SemaphoreRegistry()
|
||||
set := r.FindByID(id)
|
||||
if set == nil {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
vals := make([]uint16, set.Size())
|
||||
if _, err := t.CopyIn(array, vals); err != nil {
|
||||
return err
|
||||
}
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
return set.SetValAll(t, vals, creds)
|
||||
}
|
||||
|
||||
func getVal(t *kernel.Task, id int32, num int32) (int16, error) {
|
||||
r := t.IPCNamespace().SemaphoreRegistry()
|
||||
set := r.FindByID(id)
|
||||
@@ -164,3 +187,18 @@ func getVal(t *kernel.Task, id int32, num int32) (int16, error) {
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
return set.GetVal(num, creds)
|
||||
}
|
||||
|
||||
func getValAll(t *kernel.Task, id int32, array usermem.Addr) error {
|
||||
r := t.IPCNamespace().SemaphoreRegistry()
|
||||
set := r.FindByID(id)
|
||||
if set == nil {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
vals, err := set.GetValAll(creds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = t.CopyOut(array, vals)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ TEST(SemaphoreTest, SemOpRemoveWithWaiter_NoRandomSave) {
|
||||
});
|
||||
|
||||
// This must happen before IPC_RMID runs above. Otherwise it fails with EINVAL
|
||||
// instead because the semaphire has already been removed.
|
||||
// instead because the semaphore has already been removed.
|
||||
struct sembuf buf = {};
|
||||
buf.sem_op = -1;
|
||||
ASSERT_THAT(RetryEINTR(semop)(sem.get(), &buf, 1),
|
||||
@@ -408,6 +408,29 @@ TEST(SemaphoreTest, SemCtlVal) {
|
||||
thZero.Join();
|
||||
}
|
||||
|
||||
TEST(SemaphoreTest, SemCtlValAll) {
|
||||
AutoSem sem(semget(IPC_PRIVATE, 3, 0600 | IPC_CREAT));
|
||||
ASSERT_THAT(sem.get(), SyscallSucceeds());
|
||||
|
||||
// Semaphores must start with 0.
|
||||
uint16_t get[3] = {10, 10, 10};
|
||||
EXPECT_THAT(semctl(sem.get(), 1, GETALL, get), SyscallSucceedsWithValue(0));
|
||||
for (auto v : get) {
|
||||
EXPECT_EQ(v, 0);
|
||||
}
|
||||
|
||||
// SetAll and check that they were set.
|
||||
uint16_t vals[3] = {0, 10, 20};
|
||||
EXPECT_THAT(semctl(sem.get(), 1, SETALL, vals), SyscallSucceedsWithValue(0));
|
||||
EXPECT_THAT(semctl(sem.get(), 1, GETALL, get), SyscallSucceedsWithValue(0));
|
||||
for (size_t i = 0; i < ABSL_ARRAYSIZE(vals); ++i) {
|
||||
EXPECT_EQ(get[i], vals[i]);
|
||||
}
|
||||
|
||||
EXPECT_THAT(semctl(sem.get(), 1, SETALL, nullptr),
|
||||
SyscallFailsWithErrno(EFAULT));
|
||||
}
|
||||
|
||||
TEST(SemaphoreTest, SemIpcSet) {
|
||||
// Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions.
|
||||
ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false));
|
||||
|
||||
Reference in New Issue
Block a user