mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add semctl(GETPID) syscall
Also added unimplemented notification for semctl(2) commands. PiperOrigin-RevId: 236340672 Change-Id: I0795e3bd2e6d41d7936fabb731884df426a42478
This commit is contained in:
committed by
Shentubot
parent
7693b7469f
commit
3dbd4a16f8
@@ -27,8 +27,9 @@ const (
|
||||
|
||||
// ipcs ctl cmds. Source: include/uapi/linux/sem.h
|
||||
const (
|
||||
SEM_STAT = 18
|
||||
SEM_INFO = 19
|
||||
SEM_STAT = 18
|
||||
SEM_INFO = 19
|
||||
SEM_STAT_ANY = 20
|
||||
)
|
||||
|
||||
const SEM_UNDO = 0x1000
|
||||
|
||||
@@ -92,6 +92,7 @@ type Set struct {
|
||||
type sem struct {
|
||||
value int16
|
||||
waiters waiterList `state:"zerovalue"`
|
||||
pid int32
|
||||
}
|
||||
|
||||
// waiter represents a caller that is waiting for the semaphore value to
|
||||
@@ -283,7 +284,7 @@ func (s *Set) Change(ctx context.Context, creds *auth.Credentials, owner fs.File
|
||||
}
|
||||
|
||||
// SetVal overrides a semaphore value, waking up waiters as needed.
|
||||
func (s *Set) SetVal(ctx context.Context, num int32, val int16, creds *auth.Credentials) error {
|
||||
func (s *Set) SetVal(ctx context.Context, num int32, val int16, creds *auth.Credentials, pid int32) error {
|
||||
if val < 0 || val > valueMax {
|
||||
return syserror.ERANGE
|
||||
}
|
||||
@@ -303,15 +304,17 @@ func (s *Set) SetVal(ctx context.Context, num int32, val int16, creds *auth.Cred
|
||||
|
||||
// TODO: Clear undo entries in all processes
|
||||
sem.value = val
|
||||
sem.pid = pid
|
||||
s.changeTime = ktime.NowFromContext(ctx)
|
||||
sem.wakeWaiters()
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetValAll overrides all semaphores values, waking up waiters as needed.
|
||||
// SetValAll overrides all semaphores values, waking up waiters as needed. It also
|
||||
// sets semaphore's PID which was fixed in Linux 4.6.
|
||||
//
|
||||
// 'len(vals)' must be equal to 's.Size()'.
|
||||
func (s *Set) SetValAll(ctx context.Context, vals []uint16, creds *auth.Credentials) error {
|
||||
func (s *Set) SetValAll(ctx context.Context, vals []uint16, creds *auth.Credentials, pid int32) error {
|
||||
if len(vals) != s.Size() {
|
||||
panic(fmt.Sprintf("vals length (%d) different that Set.Size() (%d)", len(vals), s.Size()))
|
||||
}
|
||||
@@ -335,6 +338,7 @@ func (s *Set) SetValAll(ctx context.Context, vals []uint16, creds *auth.Credenti
|
||||
|
||||
// TODO: Clear undo entries in all processes
|
||||
sem.value = int16(val)
|
||||
sem.pid = pid
|
||||
sem.wakeWaiters()
|
||||
}
|
||||
s.changeTime = ktime.NowFromContext(ctx)
|
||||
@@ -375,12 +379,29 @@ func (s *Set) GetValAll(creds *auth.Credentials) ([]uint16, error) {
|
||||
return vals, nil
|
||||
}
|
||||
|
||||
// GetPID returns the PID set when performing operations in the semaphore.
|
||||
func (s *Set) GetPID(num int32, creds *auth.Credentials) (int32, 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 0, syserror.EACCES
|
||||
}
|
||||
|
||||
sem := s.findSem(num)
|
||||
if sem == nil {
|
||||
return 0, syserror.ERANGE
|
||||
}
|
||||
return sem.pid, 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.
|
||||
//
|
||||
// On failure, it may return an error (retries are hopeless) or it may return
|
||||
// a channel that can be waited on before attempting again.
|
||||
func (s *Set) ExecuteOps(ctx context.Context, ops []linux.Sembuf, creds *auth.Credentials) (chan struct{}, int32, error) {
|
||||
func (s *Set) ExecuteOps(ctx context.Context, ops []linux.Sembuf, creds *auth.Credentials, pid int32) (chan struct{}, int32, error) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
@@ -404,14 +425,14 @@ func (s *Set) ExecuteOps(ctx context.Context, ops []linux.Sembuf, creds *auth.Cr
|
||||
return nil, 0, syserror.EACCES
|
||||
}
|
||||
|
||||
ch, num, err := s.executeOps(ctx, ops)
|
||||
ch, num, err := s.executeOps(ctx, ops, pid)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return ch, num, nil
|
||||
}
|
||||
|
||||
func (s *Set) executeOps(ctx context.Context, ops []linux.Sembuf) (chan struct{}, int32, error) {
|
||||
func (s *Set) executeOps(ctx context.Context, ops []linux.Sembuf, pid int32) (chan struct{}, int32, error) {
|
||||
// Changes to semaphores go to this slice temporarily until they all succeed.
|
||||
tmpVals := make([]int16, len(s.sems))
|
||||
for i := range s.sems {
|
||||
@@ -464,6 +485,7 @@ func (s *Set) executeOps(ctx context.Context, ops []linux.Sembuf) (chan struct{}
|
||||
for i, v := range tmpVals {
|
||||
s.sems[i].value = v
|
||||
s.sems[i].wakeWaiters()
|
||||
s.sems[i].pid = pid
|
||||
}
|
||||
s.opTime = ktime.NowFromContext(ctx)
|
||||
return nil, 0, nil
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
func executeOps(ctx context.Context, t *testing.T, set *Set, ops []linux.Sembuf, block bool) chan struct{} {
|
||||
ch, _, err := set.executeOps(ctx, ops)
|
||||
ch, _, err := set.executeOps(ctx, ops, 123)
|
||||
if err != nil {
|
||||
t.Fatalf("ExecuteOps(ops) failed, err: %v, ops: %+v", err, ops)
|
||||
}
|
||||
@@ -123,13 +123,13 @@ func TestNoWait(t *testing.T) {
|
||||
|
||||
ops[0].SemOp = -2
|
||||
ops[0].SemFlg = linux.IPC_NOWAIT
|
||||
if _, _, err := set.executeOps(ctx, ops); err != syserror.ErrWouldBlock {
|
||||
if _, _, err := set.executeOps(ctx, ops, 123); err != syserror.ErrWouldBlock {
|
||||
t.Fatalf("ExecuteOps(ops) wrong result, got: %v, expected: %v", err, syserror.ErrWouldBlock)
|
||||
}
|
||||
|
||||
ops[0].SemOp = 0
|
||||
ops[0].SemFlg = linux.IPC_NOWAIT
|
||||
if _, _, err := set.executeOps(ctx, ops); err != syserror.ErrWouldBlock {
|
||||
if _, _, err := set.executeOps(ctx, ops, 123); err != syserror.ErrWouldBlock {
|
||||
t.Fatalf("ExecuteOps(ops) wrong result, got: %v, expected: %v", err, syserror.ErrWouldBlock)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,8 +71,9 @@ func Semop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
|
||||
}
|
||||
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
pid := t.Kernel().GlobalInit().PIDNamespace().IDOfThreadGroup(t.ThreadGroup())
|
||||
for {
|
||||
ch, num, err := set.ExecuteOps(t, ops, creds)
|
||||
ch, num, err := set.ExecuteOps(t, ops, creds, int32(pid))
|
||||
if ch == nil || err != nil {
|
||||
// We're done (either on success or a failure).
|
||||
return 0, nil, err
|
||||
@@ -123,6 +124,21 @@ func Semctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscal
|
||||
perms := fs.FilePermsFromMode(linux.FileMode(s.SemPerm.Mode & 0777))
|
||||
return 0, nil, ipcSet(t, id, auth.UID(s.SemPerm.UID), auth.GID(s.SemPerm.GID), perms)
|
||||
|
||||
case linux.GETPID:
|
||||
v, err := getPID(t, id, num)
|
||||
return uintptr(v), nil, err
|
||||
|
||||
case linux.IPC_INFO,
|
||||
linux.SEM_INFO,
|
||||
linux.IPC_STAT,
|
||||
linux.SEM_STAT,
|
||||
linux.SEM_STAT_ANY,
|
||||
linux.GETNCNT,
|
||||
linux.GETZCNT:
|
||||
|
||||
t.Kernel().EmitUnimplementedEvent(t)
|
||||
fallthrough
|
||||
|
||||
default:
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
@@ -161,7 +177,8 @@ func setVal(t *kernel.Task, id int32, num int32, val int16) error {
|
||||
return syserror.EINVAL
|
||||
}
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
return set.SetVal(t, num, val, creds)
|
||||
pid := t.Kernel().GlobalInit().PIDNamespace().IDOfThreadGroup(t.ThreadGroup())
|
||||
return set.SetVal(t, num, val, creds, int32(pid))
|
||||
}
|
||||
|
||||
func setValAll(t *kernel.Task, id int32, array usermem.Addr) error {
|
||||
@@ -175,7 +192,8 @@ func setValAll(t *kernel.Task, id int32, array usermem.Addr) error {
|
||||
return err
|
||||
}
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
return set.SetValAll(t, vals, creds)
|
||||
pid := t.Kernel().GlobalInit().PIDNamespace().IDOfThreadGroup(t.ThreadGroup())
|
||||
return set.SetValAll(t, vals, creds, int32(pid))
|
||||
}
|
||||
|
||||
func getVal(t *kernel.Task, id int32, num int32) (int16, error) {
|
||||
@@ -202,3 +220,22 @@ func getValAll(t *kernel.Task, id int32, array usermem.Addr) error {
|
||||
_, err = t.CopyOut(array, vals)
|
||||
return err
|
||||
}
|
||||
|
||||
func getPID(t *kernel.Task, id int32, num int32) (int32, error) {
|
||||
r := t.IPCNamespace().SemaphoreRegistry()
|
||||
set := r.FindByID(id)
|
||||
if set == nil {
|
||||
return 0, syserror.EINVAL
|
||||
}
|
||||
creds := auth.CredentialsFromContext(t)
|
||||
gpid, err := set.GetPID(num, creds)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Convert pid from init namespace to the caller's namespace.
|
||||
tg := t.PIDNamespace().ThreadGroupWithID(kernel.ThreadID(gpid))
|
||||
if tg == nil {
|
||||
return 0, nil
|
||||
}
|
||||
return int32(tg.ID()), nil
|
||||
}
|
||||
|
||||
@@ -100,6 +100,10 @@ func (c *compatEmitter) Emit(msg proto.Message) (hangup bool, err error) {
|
||||
// args: fd, level, name, ...
|
||||
tr = newArgsTracker(1, 2)
|
||||
|
||||
case syscall.SYS_SEMCTL:
|
||||
// args: semid, semnum, cmd, ...
|
||||
tr = newArgsTracker(2)
|
||||
|
||||
default:
|
||||
tr = &onceTracker{}
|
||||
}
|
||||
|
||||
@@ -431,6 +431,35 @@ TEST(SemaphoreTest, SemCtlValAll) {
|
||||
SyscallFailsWithErrno(EFAULT));
|
||||
}
|
||||
|
||||
TEST(SemaphoreTest, SemCtlGetPid) {
|
||||
AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT));
|
||||
ASSERT_THAT(sem.get(), SyscallSucceeds());
|
||||
|
||||
ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 1), SyscallSucceeds());
|
||||
EXPECT_THAT(semctl(sem.get(), 0, GETPID), SyscallSucceedsWithValue(getpid()));
|
||||
}
|
||||
|
||||
TEST(SemaphoreTest, SemCtlGetPidFork) {
|
||||
AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT));
|
||||
ASSERT_THAT(sem.get(), SyscallSucceeds());
|
||||
|
||||
const pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
ASSERT_THAT(semctl(sem.get(), 0, SETVAL, 1), SyscallSucceeds());
|
||||
ASSERT_THAT(semctl(sem.get(), 0, GETPID),
|
||||
SyscallSucceedsWithValue(getpid()));
|
||||
|
||||
_exit(0);
|
||||
}
|
||||
ASSERT_THAT(child_pid, SyscallSucceeds());
|
||||
|
||||
int status;
|
||||
ASSERT_THAT(RetryEINTR(waitpid)(child_pid, &status, 0),
|
||||
SyscallSucceedsWithValue(child_pid));
|
||||
EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0)
|
||||
<< " status " << status;
|
||||
}
|
||||
|
||||
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