diff options
Diffstat (limited to 'pkg/sentry/syscalls/linux/sys_sem.go')
-rw-r--r-- | pkg/sentry/syscalls/linux/sys_sem.go | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/pkg/sentry/syscalls/linux/sys_sem.go b/pkg/sentry/syscalls/linux/sys_sem.go new file mode 100644 index 000000000..5bd61ab87 --- /dev/null +++ b/pkg/sentry/syscalls/linux/sys_sem.go @@ -0,0 +1,241 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package linux + +import ( + "math" + + "gvisor.googlesource.com/gvisor/pkg/abi/linux" + "gvisor.googlesource.com/gvisor/pkg/sentry/arch" + "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" +) + +const opsMax = 500 // SEMOPM + +// Semget handles: semget(key_t key, int nsems, int semflg) +func Semget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + key := args[0].Int() + nsems := args[1].Int() + flag := args[2].Int() + + private := key == linux.IPC_PRIVATE + create := flag&linux.IPC_CREAT == linux.IPC_CREAT + exclusive := flag&linux.IPC_EXCL == linux.IPC_EXCL + mode := linux.FileMode(flag & 0777) + + r := t.IPCNamespace().SemaphoreRegistry() + set, err := r.FindOrCreate(t, key, nsems, mode, private, create, exclusive) + if err != nil { + return 0, nil, err + } + return uintptr(set.ID), nil, nil +} + +// Semop handles: semop(int semid, struct sembuf *sops, size_t nsops) +func Semop(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + id := args[0].Int() + sembufAddr := args[1].Pointer() + nsops := args[2].SizeT() + + r := t.IPCNamespace().SemaphoreRegistry() + set := r.FindByID(id) + if set == nil { + return 0, nil, syserror.EINVAL + } + if nsops <= 0 { + return 0, nil, syserror.EINVAL + } + if nsops > opsMax { + return 0, nil, syserror.E2BIG + } + + ops := make([]linux.Sembuf, nsops) + if _, err := t.CopyIn(sembufAddr, ops); err != nil { + return 0, nil, err + } + + creds := auth.CredentialsFromContext(t) + pid := t.Kernel().GlobalInit().PIDNamespace().IDOfThreadGroup(t.ThreadGroup()) + for { + 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 + } + if err = t.Block(ch); err != nil { + set.AbortWait(num, ch) + return 0, nil, err + } + } +} + +// Semctl handles: semctl(int semid, int semnum, int cmd, ...) +func Semctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) { + id := args[0].Int() + num := args[1].Int() + cmd := args[2].Int() + + switch cmd { + case linux.SETVAL: + val := args[3].Int() + if val > math.MaxInt16 { + return 0, nil, syserror.ERANGE + } + 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) + + case linux.IPC_SET: + arg := args[3].Pointer() + s := linux.SemidDS{} + if _, err := t.CopyIn(arg, &s); err != nil { + return 0, nil, err + } + + 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 + } +} + +func remove(t *kernel.Task, id int32) error { + r := t.IPCNamespace().SemaphoreRegistry() + creds := auth.CredentialsFromContext(t) + return r.RemoveID(id, creds) +} + +func ipcSet(t *kernel.Task, id int32, uid auth.UID, gid auth.GID, perms fs.FilePermissions) error { + r := t.IPCNamespace().SemaphoreRegistry() + set := r.FindByID(id) + if set == nil { + return syserror.EINVAL + } + + creds := auth.CredentialsFromContext(t) + kuid := creds.UserNamespace.MapToKUID(uid) + if !kuid.Ok() { + return syserror.EINVAL + } + kgid := creds.UserNamespace.MapToKGID(gid) + if !kgid.Ok() { + return syserror.EINVAL + } + owner := fs.FileOwner{UID: kuid, GID: kgid} + return set.Change(t, creds, owner, perms) +} + +func setVal(t *kernel.Task, id int32, num int32, val int16) error { + r := t.IPCNamespace().SemaphoreRegistry() + set := r.FindByID(id) + if set == nil { + return syserror.EINVAL + } + creds := auth.CredentialsFromContext(t) + 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 { + 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) + 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) { + r := t.IPCNamespace().SemaphoreRegistry() + set := r.FindByID(id) + if set == nil { + return 0, syserror.EINVAL + } + 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 +} + +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 +} |