Files
visor/pkg/sentry/kernel/semaphore/semaphore.go
T
Ayush RanjanandgVisor bot 1003ae8e93 Delete VFS1 completely.
- Delete pkg/sentry/fs/*.
- Move pkg/sentry/fs/fsutil out of VFS1 directory and remove VFS1 components.
- Remove remaining unused references to VFS1 from remaining codebase.
- Rename/refactor code to avoid even referencing VFS2, unless necessary.
- Rewrite VFS1-only tests to VFS2.

Updates #1624

PiperOrigin-RevId: 490064269
2022-11-21 13:57:52 -08:00

695 lines
17 KiB
Go

// 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 semaphore implements System V semaphores.
package semaphore
import (
"fmt"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/kernel/ipc"
ktime "gvisor.dev/gvisor/pkg/sentry/kernel/time"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/sync"
)
const (
// Maximum semaphore value.
valueMax = linux.SEMVMX
// Maximum number of semaphore sets.
setsMax = linux.SEMMNI
// Maximum number of semaphores in a semaphore set.
semsMax = linux.SEMMSL
// Maximum number of semaphores in all semaphore sets.
semsTotalMax = linux.SEMMNS
)
// Registry maintains a set of semaphores that can be found by key or ID.
//
// +stateify savable
type Registry struct {
// mu protects all fields below.
mu sync.Mutex `state:"nosave"`
// reg defines basic fields and operations needed for all SysV registries.
reg *ipc.Registry
// indexes maintains a mapping between a set's index in virtual array and
// its identifier.
indexes map[int32]ipc.ID
}
// Set represents a set of semaphores that can be operated atomically.
//
// +stateify savable
type Set struct {
// registry owning this sem set. Immutable.
registry *Registry
// mu protects all fields below.
mu sync.Mutex `state:"nosave"`
obj *ipc.Object
opTime ktime.Time
changeTime ktime.Time
// 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.
dead bool
}
// sem represents a single semaphore from a set.
//
// +stateify savable
type sem struct {
value int16
waiters waiterList `state:"zerovalue"`
pid int32
}
// waiter represents a caller that is waiting for the semaphore value to
// become positive or zero.
//
// +stateify savable
type waiter struct {
waiterEntry
// value represents how much resource the waiter needs to wake up.
// The value is either 0 or negative.
value int16
ch chan struct{}
}
// NewRegistry creates a new semaphore set registry.
func NewRegistry(userNS *auth.UserNamespace) *Registry {
return &Registry{
reg: ipc.NewRegistry(userNS),
indexes: make(map[int32]ipc.ID),
}
}
// FindOrCreate searches for a semaphore set that matches 'key'. If not found,
// it may create a new one if requested. If private is true, key is ignored and
// a new set is always created. If create is false, it fails if a set cannot
// be found. If exclusive is true, it fails if a set with the same key already
// exists.
func (r *Registry) FindOrCreate(ctx context.Context, key ipc.Key, nsems int32, mode linux.FileMode, private, create, exclusive bool) (*Set, error) {
if nsems < 0 || nsems > semsMax {
return nil, linuxerr.EINVAL
}
r.mu.Lock()
defer r.mu.Unlock()
if !private {
set, err := r.reg.Find(ctx, key, mode, create, exclusive)
if err != nil {
return nil, err
}
// Validate semaphore-specific parameters.
if set != nil {
set := set.(*Set)
if nsems > int32(set.Size()) {
return nil, linuxerr.EINVAL
}
return set, nil
}
}
// Zero is only valid if an existing set is found.
if nsems == 0 {
return nil, linuxerr.EINVAL
}
// Apply system limits.
//
// Map reg.objects and map indexes in a registry are of the same size,
// check map reg.objects only here for the system limit.
if r.reg.ObjectCount() >= setsMax {
return nil, linuxerr.ENOSPC
}
if r.totalSems() > int(semsTotalMax-nsems) {
return nil, linuxerr.ENOSPC
}
// Finally create a new set.
return r.newSetLocked(ctx, key, auth.CredentialsFromContext(ctx), mode, nsems)
}
// IPCInfo returns information about system-wide semaphore limits and parameters.
func (r *Registry) IPCInfo() *linux.SemInfo {
return &linux.SemInfo{
SemMap: linux.SEMMAP,
SemMni: linux.SEMMNI,
SemMns: linux.SEMMNS,
SemMnu: linux.SEMMNU,
SemMsl: linux.SEMMSL,
SemOpm: linux.SEMOPM,
SemUme: linux.SEMUME,
SemUsz: linux.SEMUSZ,
SemVmx: linux.SEMVMX,
SemAem: linux.SEMAEM,
}
}
// SemInfo returns a seminfo structure containing the same information as
// for IPC_INFO, except that SemUsz field returns the number of existing
// semaphore sets, and SemAem field returns the number of existing semaphores.
func (r *Registry) SemInfo() *linux.SemInfo {
r.mu.Lock()
defer r.mu.Unlock()
info := r.IPCInfo()
info.SemUsz = uint32(r.reg.ObjectCount())
info.SemAem = uint32(r.totalSems())
return info
}
// 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 semaphore set.
var highestIndex int32
for index := range r.indexes {
if index > highestIndex {
highestIndex = index
}
}
return highestIndex
}
// Remove removes set with give 'id' from the registry and marks the set as
// dead. All waiters will be awakened and fail.
func (r *Registry) Remove(id ipc.ID, creds *auth.Credentials) error {
r.mu.Lock()
defer r.mu.Unlock()
index, found := r.findIndexByID(id)
if !found {
return linuxerr.EINVAL
}
delete(r.indexes, index)
r.reg.Remove(id, creds)
return nil
}
// newSetLocked creates a new Set using given fields. An error is returned if there
// are no more available identifiers.
//
// Precondition: r.mu must be held.
func (r *Registry) newSetLocked(ctx context.Context, key ipc.Key, creator *auth.Credentials, mode linux.FileMode, nsems int32) (*Set, error) {
set := &Set{
registry: r,
obj: ipc.NewObject(r.reg.UserNS, ipc.Key(key), creator, creator, mode),
changeTime: ktime.NowFromContext(ctx),
sems: make([]sem, nsems),
}
err := r.reg.Register(set)
if err != nil {
return nil, err
}
index, found := r.findFirstAvailableIndex()
if !found {
// See linux, ipc/sem.c:newary().
return nil, linuxerr.ENOSPC
}
r.indexes[index] = set.obj.ID
return set, nil
}
// FindByID looks up a set given an ID.
func (r *Registry) FindByID(id ipc.ID) *Set {
r.mu.Lock()
defer r.mu.Unlock()
mech := r.reg.FindByID(id)
if mech == nil {
return nil
}
return mech.(*Set)
}
// FindByIndex looks up a set given an index.
func (r *Registry) FindByIndex(index int32) *Set {
r.mu.Lock()
defer r.mu.Unlock()
id, present := r.indexes[index]
if !present {
return nil
}
return r.reg.FindByID(id).(*Set)
}
func (r *Registry) findIndexByID(id ipc.ID) (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
r.reg.ForAllObjects(
func(o ipc.Mechanism) {
totalSems += o.(*Set).Size()
},
)
return totalSems
}
// ID returns semaphore's ID.
func (s *Set) ID() ipc.ID {
return s.obj.ID
}
// Object implements ipc.Mechanism.Object.
func (s *Set) Object() *ipc.Object {
return s.obj
}
// Lock implements ipc.Mechanism.Lock.
func (s *Set) Lock() {
s.mu.Lock()
}
// Unlock implements ipc.mechanism.Unlock.
//
// +checklocksignore
func (s *Set) Unlock() {
s.mu.Unlock()
}
func (s *Set) findSem(num int32) *sem {
if num < 0 || int(num) >= s.Size() {
return nil
}
return &s.sems[num]
}
// Size returns the number of semaphores in the set. Size is immutable.
func (s *Set) Size() int {
return len(s.sems)
}
// Set modifies attributes for a semaphore set. See semctl(IPC_SET).
func (s *Set) Set(ctx context.Context, ds *linux.SemidDS) error {
s.mu.Lock()
defer s.mu.Unlock()
if err := s.obj.Set(ctx, &ds.SemPerm); err != nil {
return err
}
s.changeTime = ktime.NowFromContext(ctx)
return nil
}
// 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, vfs.MayRead)
}
// 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, 0)
}
func (s *Set) semStat(creds *auth.Credentials, ats vfs.AccessTypes) (*linux.SemidDS, error) {
s.mu.Lock()
defer s.mu.Unlock()
if !s.obj.CheckPermissions(creds, ats) {
return nil, linuxerr.EACCES
}
return &linux.SemidDS{
SemPerm: linux.IPCPerm{
Key: uint32(s.obj.Key),
UID: uint32(creds.UserNamespace.MapFromKUID(s.obj.OwnerUID)),
GID: uint32(creds.UserNamespace.MapFromKGID(s.obj.OwnerGID)),
CUID: uint32(creds.UserNamespace.MapFromKUID(s.obj.CreatorUID)),
CGID: uint32(creds.UserNamespace.MapFromKGID(s.obj.CreatorGID)),
Mode: uint16(s.obj.Mode),
Seq: 0, // IPC sequence not supported.
},
SemOTime: s.opTime.TimeT(),
SemCTime: s.changeTime.TimeT(),
SemNSems: uint64(s.Size()),
}, nil
}
// SetVal overrides a semaphore value, waking up waiters as needed.
func (s *Set) SetVal(ctx context.Context, num int32, val int16, creds *auth.Credentials, pid int32) error {
if val < 0 || val > valueMax {
return linuxerr.ERANGE
}
s.mu.Lock()
defer s.mu.Unlock()
// "The calling process must have alter permission on the semaphore set."
if !s.obj.CheckPermissions(creds, vfs.MayWrite) {
return linuxerr.EACCES
}
sem := s.findSem(num)
if sem == nil {
return linuxerr.ERANGE
}
// TODO(gvisor.dev/issue/137): 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. 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, pid int32) 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 > valueMax {
return linuxerr.ERANGE
}
}
s.mu.Lock()
defer s.mu.Unlock()
// "The calling process must have alter permission on the semaphore set."
if !s.obj.CheckPermissions(creds, vfs.MayWrite) {
return linuxerr.EACCES
}
for i, val := range vals {
sem := &s.sems[i]
// TODO(gvisor.dev/issue/137): Clear undo entries in all processes.
sem.value = int16(val)
sem.pid = pid
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()
defer s.mu.Unlock()
// "The calling process must have read permission on the semaphore set."
if !s.obj.CheckPermissions(creds, vfs.MayRead) {
return 0, linuxerr.EACCES
}
sem := s.findSem(num)
if sem == nil {
return 0, linuxerr.ERANGE
}
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.obj.CheckPermissions(creds, vfs.MayRead) {
return nil, linuxerr.EACCES
}
vals := make([]uint16, s.Size())
for i, sem := range s.sems {
vals[i] = uint16(sem.value)
}
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.obj.CheckPermissions(creds, vfs.MayRead) {
return 0, linuxerr.EACCES
}
sem := s.findSem(num)
if sem == nil {
return 0, linuxerr.ERANGE
}
return sem.pid, nil
}
func (s *Set) countWaiters(num int32, creds *auth.Credentials, pred func(w *waiter) bool) (uint16, error) {
s.mu.Lock()
defer s.mu.Unlock()
// The calling process must have read permission on the semaphore set.
if !s.obj.CheckPermissions(creds, vfs.MayRead) {
return 0, linuxerr.EACCES
}
sem := s.findSem(num)
if sem == nil {
return 0, linuxerr.ERANGE
}
var cnt uint16
for w := sem.waiters.Front(); w != nil; w = w.Next() {
if pred(w) {
cnt++
}
}
return cnt, nil
}
// CountZeroWaiters returns number of waiters waiting for the sem's value to increase.
func (s *Set) CountZeroWaiters(num int32, creds *auth.Credentials) (uint16, error) {
return s.countWaiters(num, creds, func(w *waiter) bool {
return w.value == 0
})
}
// CountNegativeWaiters returns number of waiters waiting for the sem to go to zero.
func (s *Set) CountNegativeWaiters(num int32, creds *auth.Credentials) (uint16, error) {
return s.countWaiters(num, creds, func(w *waiter) bool {
return w.value < 0
})
}
// 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, pid int32) (chan struct{}, int32, error) {
s.mu.Lock()
defer s.mu.Unlock()
// Did it race with a removal operation?
if s.dead {
return nil, 0, linuxerr.EIDRM
}
// Validate the operations.
readOnly := true
for _, op := range ops {
if s.findSem(int32(op.SemNum)) == nil {
return nil, 0, linuxerr.EFBIG
}
if op.SemOp != 0 {
readOnly = false
}
}
ats := vfs.MayRead
if !readOnly {
ats = vfs.MayWrite
}
if !s.obj.CheckPermissions(creds, ats) {
return nil, 0, linuxerr.EACCES
}
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, 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 {
tmpVals[i] = s.sems[i].value
}
for _, op := range ops {
sem := &s.sems[op.SemNum]
if op.SemOp == 0 {
// Handle 'wait for zero' operation.
if tmpVals[op.SemNum] != 0 {
// Semaphore isn't 0, must wait.
if op.SemFlg&linux.IPC_NOWAIT != 0 {
return nil, 0, linuxerr.ErrWouldBlock
}
w := newWaiter(op.SemOp)
sem.waiters.PushBack(w)
return w.ch, int32(op.SemNum), nil
}
} else {
if op.SemOp < 0 {
// Handle 'wait' operation.
if -op.SemOp > valueMax {
return nil, 0, linuxerr.ERANGE
}
if -op.SemOp > tmpVals[op.SemNum] {
// Not enough resources, must wait.
if op.SemFlg&linux.IPC_NOWAIT != 0 {
return nil, 0, linuxerr.ErrWouldBlock
}
w := newWaiter(op.SemOp)
sem.waiters.PushBack(w)
return w.ch, int32(op.SemNum), nil
}
} else {
// op.SemOp > 0: Handle 'signal' operation.
if tmpVals[op.SemNum] > valueMax-op.SemOp {
return nil, 0, linuxerr.ERANGE
}
}
tmpVals[op.SemNum] += op.SemOp
}
}
// All operations succeeded, apply them.
// TODO(gvisor.dev/issue/137): handle undo operations.
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
}
// AbortWait notifies that a waiter is giving up and will not wait on the
// channel anymore.
func (s *Set) AbortWait(num int32, ch chan struct{}) {
s.mu.Lock()
defer s.mu.Unlock()
sem := &s.sems[num]
for w := sem.waiters.Front(); w != nil; w = w.Next() {
if w.ch == ch {
sem.waiters.Remove(w)
return
}
}
// Waiter may not be found in case it raced with wakeWaiters().
}
// Destroy implements ipc.Mechanism.Destroy.
//
// Preconditions: Caller must hold 's.mu'.
func (s *Set) Destroy() {
// Notify all waiters. They will fail on the next attempt to execute
// operations and return error.
s.dead = true
for _, s := range s.sems {
for w := s.waiters.Front(); w != nil; w = w.Next() {
w.ch <- struct{}{}
}
s.waiters.Reset()
}
}
func abs(val int16) int16 {
if val < 0 {
return -val
}
return val
}
// wakeWaiters goes over all waiters and checks which of them can be notified.
func (s *sem) wakeWaiters() {
// Note that this will release all waiters waiting for 0 too.
for w := s.waiters.Front(); w != nil; {
if s.value < abs(w.value) {
// Still blocked, skip it.
w = w.Next()
continue
}
w.ch <- struct{}{}
old := w
w = w.Next()
s.waiters.Remove(old)
}
}
func newWaiter(val int16) *waiter {
return &waiter{
value: val,
ch: make(chan struct{}, 1),
}
}