// Copyright 2021 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 ipc import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/context" "gvisor.dev/gvisor/pkg/errors/linuxerr" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/sentry/fs" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" ) // Registry is similar to Object, but for registries. It represent an abstract // SysV IPC registry with fields common to all SysV registries. Registry is not // thread-safe, and should be protected using a mutex. // // +stateify savable type Registry struct { // UserNS owning the IPC namespace this registry belongs to. Immutable. UserNS *auth.UserNamespace // objects is a map of IDs to IPC mechanisms. objects map[ID]Mechanism // KeysToIDs maps a lookup key to an ID. keysToIDs map[Key]ID // lastIDUsed is used to find the next available ID for object creation. lastIDUsed ID } // NewRegistry return a new, initialized ipc.Registry. func NewRegistry(userNS *auth.UserNamespace) *Registry { return &Registry{ UserNS: userNS, objects: make(map[ID]Mechanism), keysToIDs: make(map[Key]ID), } } // Find uses key to search for and return a SysV mechanism. Find returns an // error if an object is found by shouldn't be, or if the user doesn't have // permission to use the object. If no object is found, Find checks create // flag, and returns an error only if it's false. func (r *Registry) Find(ctx context.Context, key Key, mode linux.FileMode, create, exclusive bool) (Mechanism, error) { if id, ok := r.keysToIDs[key]; ok { mech := r.objects[id] mech.Lock() defer mech.Unlock() obj := mech.Object() creds := auth.CredentialsFromContext(ctx) if !obj.CheckPermissions(creds, fs.PermsFromMode(mode)) { // The [calling process / user] does not have permission to access // the set, and does not have the CAP_IPC_OWNER capability in the // user namespace that governs its IPC namespace. return nil, linuxerr.EACCES } if create && exclusive { // IPC_CREAT and IPC_EXCL were specified, but an object already // exists for key. return nil, linuxerr.EEXIST } return mech, nil } if !create { // No object exists for key and msgflg did not specify IPC_CREAT. return nil, linuxerr.ENOENT } return nil, nil } // Register adds the given object into Registry.Objects, and assigns it a new // ID. It returns an error if all IDs are exhausted. func (r *Registry) Register(m Mechanism) error { id, err := r.newID() if err != nil { return err } obj := m.Object() obj.ID = id r.objects[id] = m r.keysToIDs[obj.Key] = id return nil } // newID finds the first unused ID in the registry, and returns an error if // non is found. func (r *Registry) newID() (ID, error) { // Find the next available ID. for id := r.lastIDUsed + 1; id != r.lastIDUsed; id++ { // Handle wrap around. if id < 0 { id = 0 continue } if r.objects[id] == nil { r.lastIDUsed = id return id, nil } } log.Warningf("ids exhausted, they may be leaking") // The man pages for shmget(2) mention that ENOSPC should be used if "All // possible shared memory IDs have been taken (SHMMNI)". Other SysV // mechanisms don't have a specific errno for running out of IDs, but they // return ENOSPC if the max number of objects is exceeded, so we assume that // it's the same case. return 0, linuxerr.ENOSPC } // Remove removes the mechanism with the given id from the registry, and calls // mechanism.Destroy to perform mechanism-specific removal. func (r *Registry) Remove(id ID, creds *auth.Credentials) error { mech := r.objects[id] if mech == nil { return linuxerr.EINVAL } mech.Lock() defer mech.Unlock() obj := mech.Object() // The effective user ID of the calling process must match the creator or // owner of the [mechanism], or the caller must be privileged. if !obj.CheckOwnership(creds) { return linuxerr.EPERM } delete(r.objects, obj.ID) delete(r.keysToIDs, obj.Key) mech.Destroy() return nil } // ForAllObjects executes a given function for all given objects. func (r *Registry) ForAllObjects(f func(o Mechanism)) { for _, o := range r.objects { f(o) } } // FindByID returns the mechanism with the given ID, nil if non exists. func (r *Registry) FindByID(id ID) Mechanism { return r.objects[id] } // DissociateKey removes the association between a mechanism and its key // (deletes it from r.keysToIDs), preventing it from being discovered by any new // process, but not necessarily destroying it. If the given key doesn't exist, // nothing is changed. func (r *Registry) DissociateKey(key Key) { delete(r.keysToIDs, key) } // DissociateID removes the association between a mechanism and its ID (deletes // it from r.objects). An ID can't be removed unless the associated key is // removed already, this is done to prevent the users from acquiring nil a // Mechanism. // // Precondition: must be preceded by a call to r.DissociateKey. func (r *Registry) DissociateID(id ID) { delete(r.objects, id) } // ObjectCount returns the number of registered objects. func (r *Registry) ObjectCount() int { return len(r.objects) } // LastIDUsed returns the last used ID. func (r *Registry) LastIDUsed() ID { return r.lastIDUsed }