mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement sysv shm.
PiperOrigin-RevId: 197058289 Change-Id: I3946c25028b7e032be4894d61acb48ac0c24d574
This commit is contained in:
@@ -51,6 +51,7 @@ go_library(
|
||||
"sched.go",
|
||||
"seccomp.go",
|
||||
"sem.go",
|
||||
"shm.go",
|
||||
"signal.go",
|
||||
"socket.go",
|
||||
"time.go",
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2018 Google Inc.
|
||||
//
|
||||
// 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
|
||||
|
||||
// shmat(2) flags. Source: include/uapi/linux/shm.h
|
||||
const (
|
||||
SHM_RDONLY = 010000 // Read-only access.
|
||||
SHM_RND = 020000 // Round attach address to SHMLBA boundary.
|
||||
SHM_REMAP = 040000 // Take-over region on attach.
|
||||
SHM_EXEC = 0100000 // Execution access.
|
||||
)
|
||||
|
||||
// IPCPerm.Mode upper byte flags. Source: include/linux/shm.h
|
||||
const (
|
||||
SHM_DEST = 01000 // Segment will be destroyed on last detach.
|
||||
SHM_LOCKED = 02000 // Segment will not be swapped.
|
||||
SHM_HUGETLB = 04000 // Segment will use huge TLB pages.
|
||||
SHM_NORESERVE = 010000 // Don't check for reservations.
|
||||
)
|
||||
|
||||
// Additional Linux-only flags for shmctl(2). Source: include/uapi/linux/shm.h
|
||||
const (
|
||||
SHM_LOCK = 11
|
||||
SHM_UNLOCK = 12
|
||||
SHM_STAT = 13
|
||||
SHM_INFO = 14
|
||||
)
|
||||
|
||||
// ShmidDS is equivalent to struct shmid64_ds. Source:
|
||||
// include/uapi/asm-generic/shmbuf.h
|
||||
type ShmidDS struct {
|
||||
ShmPerm IPCPerm
|
||||
ShmSegsz uint64
|
||||
ShmAtime TimeT
|
||||
ShmDtime TimeT
|
||||
ShmCtime TimeT
|
||||
ShmCpid int32
|
||||
ShmLpid int32
|
||||
ShmNattach uint64
|
||||
|
||||
Unused4 uint64
|
||||
Unused5 uint64
|
||||
}
|
||||
|
||||
// ShmParams is equivalent to struct shminfo. Source: include/uapi/linux/shm.h
|
||||
type ShmParams struct {
|
||||
ShmMax uint64
|
||||
ShmMin uint64
|
||||
ShmMni uint64
|
||||
ShmSeg uint64
|
||||
ShmAll uint64
|
||||
}
|
||||
|
||||
// ShmInfo is equivalent to struct shm_info. Source: include/uapi/linux/shm.h
|
||||
type ShmInfo struct {
|
||||
UsedIDs int32 // Number of currently existing segments.
|
||||
_ [4]byte
|
||||
ShmTot uint64 // Total number of shared memory pages.
|
||||
ShmRss uint64 // Number of resident shared memory pages.
|
||||
ShmSwp uint64 // Number of swapped shared memory pages.
|
||||
SwapAttempts uint64 // Unused since Linux 2.4.
|
||||
SwapSuccesses uint64 // Unused since Linux 2.4.
|
||||
}
|
||||
@@ -194,9 +194,11 @@ type AtomicRefCount struct {
|
||||
weakRefs ilist.List `state:"nosave"`
|
||||
}
|
||||
|
||||
// TestReadRefs returns the current reference count of r. Use only for tests.
|
||||
func (r *AtomicRefCount) TestReadRefs() int64 {
|
||||
return atomic.LoadInt64(&r.refCount)
|
||||
// ReadRefs returns the current number of references. The returned count is
|
||||
// inherently racy and is unsafe to use without external synchronization.
|
||||
func (r *AtomicRefCount) ReadRefs() int64 {
|
||||
// Account for the internal -1 offset on refcounts.
|
||||
return atomic.LoadInt64(&r.refCount) + 1
|
||||
}
|
||||
|
||||
// IncRef increments this object's reference count. While the count is kept
|
||||
|
||||
@@ -20,6 +20,26 @@ import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/log"
|
||||
)
|
||||
|
||||
type contextID int
|
||||
|
||||
// Globally accessible values from a context. These keys are defined in the
|
||||
// context package to resolve dependency cycles by not requiring the caller to
|
||||
// import packages usually required to get these information.
|
||||
const (
|
||||
// CtxThreadGroupID is the current thread group ID when a context represents
|
||||
// a task context. The value is represented as an int32.
|
||||
CtxThreadGroupID contextID = iota
|
||||
)
|
||||
|
||||
// ThreadGroupIDFromContext returns the current thread group ID when ctx
|
||||
// represents a task context.
|
||||
func ThreadGroupIDFromContext(ctx Context) (tgid int32, ok bool) {
|
||||
if tgid := ctx.Value(CtxThreadGroupID); tgid != nil {
|
||||
return tgid.(int32), true
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// A Context represents a thread of execution (hereafter "goroutine" to reflect
|
||||
// Go idiosyncrasy). It carries state associated with the goroutine across API
|
||||
// boundaries.
|
||||
|
||||
@@ -33,8 +33,8 @@ func TestWalkPositive(t *testing.T) {
|
||||
ctx := contexttest.Context(t)
|
||||
root := NewDirent(newMockDirInode(ctx, nil), "root")
|
||||
|
||||
if got := root.TestReadRefs(); got != 0 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 0)
|
||||
if got := root.ReadRefs(); got != 1 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 1)
|
||||
}
|
||||
|
||||
name := "d"
|
||||
@@ -43,22 +43,22 @@ func TestWalkPositive(t *testing.T) {
|
||||
t.Fatalf("root.walk(root, %q) got %v, want nil", name, err)
|
||||
}
|
||||
|
||||
if got := root.TestReadRefs(); got != 1 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 1)
|
||||
if got := root.ReadRefs(); got != 2 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 2)
|
||||
}
|
||||
|
||||
if got := d.TestReadRefs(); got != 0 {
|
||||
t.Fatalf("child name = %q has a ref count of %d, want %d", d.name, got, 0)
|
||||
if got := d.ReadRefs(); got != 1 {
|
||||
t.Fatalf("child name = %q has a ref count of %d, want %d", d.name, got, 1)
|
||||
}
|
||||
|
||||
d.DecRef()
|
||||
|
||||
if got := root.TestReadRefs(); got != 0 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 0)
|
||||
if got := root.ReadRefs(); got != 1 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 1)
|
||||
}
|
||||
|
||||
if got := d.TestReadRefs(); got != -1 {
|
||||
t.Fatalf("child name = %q has a ref count of %d, want %d", d.name, got, -1)
|
||||
if got := d.ReadRefs(); got != 0 {
|
||||
t.Fatalf("child name = %q has a ref count of %d, want %d", d.name, got, 0)
|
||||
}
|
||||
|
||||
root.flush()
|
||||
@@ -76,8 +76,8 @@ func TestWalkNegative(t *testing.T) {
|
||||
root := NewDirent(NewEmptyDir(ctx, nil), "root")
|
||||
mn := root.Inode.InodeOperations.(*mockInodeOperationsLookupNegative)
|
||||
|
||||
if got := root.TestReadRefs(); got != 0 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 0)
|
||||
if got := root.ReadRefs(); got != 1 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 1)
|
||||
}
|
||||
|
||||
name := "d"
|
||||
@@ -88,7 +88,7 @@ func TestWalkNegative(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
if got := root.TestReadRefs(); got != 0 {
|
||||
if got := root.ReadRefs(); got != 1 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 1)
|
||||
}
|
||||
|
||||
@@ -110,14 +110,14 @@ func TestWalkNegative(t *testing.T) {
|
||||
t.Fatalf("root found positive child at %q, want negative", name)
|
||||
}
|
||||
|
||||
if got := child.(*Dirent).TestReadRefs(); got != 1 {
|
||||
t.Fatalf("child has a ref count of %d, want %d", got, 1)
|
||||
if got := child.(*Dirent).ReadRefs(); got != 2 {
|
||||
t.Fatalf("child has a ref count of %d, want %d", got, 2)
|
||||
}
|
||||
|
||||
child.DecRef()
|
||||
|
||||
if got := child.(*Dirent).TestReadRefs(); got != 0 {
|
||||
t.Fatalf("child has a ref count of %d, want %d", got, 0)
|
||||
if got := child.(*Dirent).ReadRefs(); got != 1 {
|
||||
t.Fatalf("child has a ref count of %d, want %d", got, 1)
|
||||
}
|
||||
|
||||
if got := len(root.children); got != 1 {
|
||||
@@ -126,7 +126,7 @@ func TestWalkNegative(t *testing.T) {
|
||||
|
||||
root.DecRef()
|
||||
|
||||
if got := root.TestReadRefs(); got != -1 {
|
||||
if got := root.ReadRefs(); got != 0 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 0)
|
||||
}
|
||||
|
||||
@@ -184,12 +184,12 @@ func TestHashNegativeToPositive(t *testing.T) {
|
||||
t.Fatalf("got negative Dirent, want positive")
|
||||
}
|
||||
|
||||
if got := d.TestReadRefs(); got != 0 {
|
||||
t.Fatalf("child %q has a ref count of %d, want %d", name, got, 0)
|
||||
if got := d.ReadRefs(); got != 1 {
|
||||
t.Fatalf("child %q has a ref count of %d, want %d", name, got, 1)
|
||||
}
|
||||
|
||||
if got := root.TestReadRefs(); got != 1 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 1)
|
||||
if got := root.ReadRefs(); got != 2 {
|
||||
t.Fatalf("root has a ref count of %d, want %d", got, 2)
|
||||
}
|
||||
|
||||
if got := len(root.children); got != 1 {
|
||||
@@ -291,12 +291,12 @@ func TestCreateExtraRefs(t *testing.T) {
|
||||
{
|
||||
desc: "Create caching",
|
||||
root: NewDirent(NewEmptyDir(ctx, NewDirentCache(1)), "root"),
|
||||
refs: 1,
|
||||
refs: 2,
|
||||
},
|
||||
{
|
||||
desc: "Create not caching",
|
||||
root: NewDirent(NewEmptyDir(ctx, nil), "root"),
|
||||
refs: 0,
|
||||
refs: 1,
|
||||
},
|
||||
} {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
@@ -307,7 +307,7 @@ func TestCreateExtraRefs(t *testing.T) {
|
||||
}
|
||||
d := f.Dirent
|
||||
|
||||
if got := d.TestReadRefs(); got != test.refs {
|
||||
if got := d.ReadRefs(); got != test.refs {
|
||||
t.Errorf("dirent has a ref count of %d, want %d", got, test.refs)
|
||||
}
|
||||
})
|
||||
@@ -347,8 +347,8 @@ func TestRemoveExtraRefs(t *testing.T) {
|
||||
t.Fatalf("root.Remove(root, %q) failed: %v", name, err)
|
||||
}
|
||||
|
||||
if got := d.TestReadRefs(); got != 0 {
|
||||
t.Fatalf("dirent has a ref count of %d, want %d", got, 0)
|
||||
if got := d.ReadRefs(); got != 1 {
|
||||
t.Fatalf("dirent has a ref count of %d, want %d", got, 1)
|
||||
}
|
||||
|
||||
d.DecRef()
|
||||
@@ -406,11 +406,11 @@ func TestRenameExtraRefs(t *testing.T) {
|
||||
newParent.flush()
|
||||
|
||||
// Expect to have only active references.
|
||||
if got := renamed.TestReadRefs(); got != 0 {
|
||||
t.Errorf("renamed has ref count %d, want only active references %d", got, 0)
|
||||
if got := renamed.ReadRefs(); got != 1 {
|
||||
t.Errorf("renamed has ref count %d, want only active references %d", got, 1)
|
||||
}
|
||||
if got := replaced.TestReadRefs(); got != 0 {
|
||||
t.Errorf("replaced has ref count %d, want only active references %d", got, 0)
|
||||
if got := replaced.ReadRefs(); got != 1 {
|
||||
t.Errorf("replaced has ref count %d, want only active references %d", got, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -184,6 +184,7 @@ go_library(
|
||||
"//pkg/sentry/kernel/kdefs",
|
||||
"//pkg/sentry/kernel/sched",
|
||||
"//pkg/sentry/kernel/semaphore",
|
||||
"//pkg/sentry/kernel/shm",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/limits",
|
||||
"//pkg/sentry/loader",
|
||||
|
||||
@@ -15,18 +15,26 @@
|
||||
package kernel
|
||||
|
||||
import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/semaphore"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/shm"
|
||||
)
|
||||
|
||||
// IPCNamespace represents an IPC namespace.
|
||||
type IPCNamespace struct {
|
||||
// User namespace which owns this IPC namespace. Immutable.
|
||||
userNS *auth.UserNamespace
|
||||
|
||||
semaphores *semaphore.Registry
|
||||
shms *shm.Registry
|
||||
}
|
||||
|
||||
// NewIPCNamespace creates a new IPC namespace.
|
||||
func NewIPCNamespace() *IPCNamespace {
|
||||
func NewIPCNamespace(userNS *auth.UserNamespace) *IPCNamespace {
|
||||
return &IPCNamespace{
|
||||
userNS: userNS,
|
||||
semaphores: semaphore.NewRegistry(),
|
||||
shms: shm.NewRegistry(userNS),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +43,11 @@ func (i *IPCNamespace) SemaphoreRegistry() *semaphore.Registry {
|
||||
return i.semaphores
|
||||
}
|
||||
|
||||
// ShmRegistry returns the shm segment registry for this namespace.
|
||||
func (i *IPCNamespace) ShmRegistry() *shm.Registry {
|
||||
return i.shms
|
||||
}
|
||||
|
||||
// IPCNamespace returns the task's IPC namespace.
|
||||
func (t *Task) IPCNamespace() *IPCNamespace {
|
||||
t.mu.Lock()
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package(licenses = ["notice"]) # Apache 2.0
|
||||
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
load("//tools/go_stateify:defs.bzl", "go_stateify")
|
||||
|
||||
go_stateify(
|
||||
name = "shm_state",
|
||||
srcs = [
|
||||
"shm.go",
|
||||
],
|
||||
out = "shm_autogen_state.go",
|
||||
package = "shm",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "shm",
|
||||
srcs = [
|
||||
"device.go",
|
||||
"shm.go",
|
||||
"shm_autogen_state.go",
|
||||
],
|
||||
importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/kernel/shm",
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/log",
|
||||
"//pkg/refs",
|
||||
"//pkg/sentry/context",
|
||||
"//pkg/sentry/device",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/memmap",
|
||||
"//pkg/sentry/platform",
|
||||
"//pkg/sentry/usage",
|
||||
"//pkg/sentry/usermem",
|
||||
"//pkg/state",
|
||||
"//pkg/syserror",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2018 Google Inc.
|
||||
//
|
||||
// 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 shm
|
||||
|
||||
import "gvisor.googlesource.com/gvisor/pkg/sentry/device"
|
||||
|
||||
// shmDevice is the kernel shm device.
|
||||
var shmDevice = device.NewAnonDevice()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@ import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
|
||||
"gvisor.googlesource.com/gvisor/pkg/bpf"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/fs"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/inet"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/auth"
|
||||
@@ -559,6 +560,8 @@ func (t *Task) Value(key interface{}) interface{} {
|
||||
return t
|
||||
case auth.CtxCredentials:
|
||||
return t.creds
|
||||
case context.CtxThreadGroupID:
|
||||
return int32(t.ThreadGroup().ID())
|
||||
case fs.CtxRoot:
|
||||
return t.FSContext().RootDirectory()
|
||||
case inet.CtxStack:
|
||||
|
||||
@@ -197,7 +197,7 @@ func (t *Task) Clone(opts *CloneOptions) (ThreadID, *SyscallControl, error) {
|
||||
if opts.NewIPCNamespace {
|
||||
// Note that "If CLONE_NEWIPC is set, then create the process in a new IPC
|
||||
// namespace"
|
||||
ipcns = NewIPCNamespace()
|
||||
ipcns = NewIPCNamespace(userns)
|
||||
}
|
||||
|
||||
tc, err := t.tc.Fork(t, !opts.NewAddressSpace)
|
||||
@@ -449,7 +449,7 @@ func (t *Task) Unshare(opts *SharingOptions) error {
|
||||
}
|
||||
// Note that "If CLONE_NEWIPC is set, then create the process in a new IPC
|
||||
// namespace"
|
||||
t.ipcns = NewIPCNamespace()
|
||||
t.ipcns = NewIPCNamespace(t.creds.UserNamespace)
|
||||
}
|
||||
if opts.NewFiles {
|
||||
oldFDMap := t.tr.FDMap
|
||||
|
||||
@@ -107,6 +107,7 @@ go_library(
|
||||
"pma_set.go",
|
||||
"proc_pid_maps.go",
|
||||
"save_restore.go",
|
||||
"shm.go",
|
||||
"special_mappable.go",
|
||||
"syscalls.go",
|
||||
"vma.go",
|
||||
@@ -123,6 +124,7 @@ go_library(
|
||||
"//pkg/sentry/context",
|
||||
"//pkg/sentry/fs",
|
||||
"//pkg/sentry/fs/proc/seqfile",
|
||||
"//pkg/sentry/kernel/shm",
|
||||
"//pkg/sentry/limits",
|
||||
"//pkg/sentry/memmap",
|
||||
"//pkg/sentry/platform",
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2018 Google Inc.
|
||||
//
|
||||
// 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 mm
|
||||
|
||||
import (
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/shm"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/usermem"
|
||||
"gvisor.googlesource.com/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
// DetachShm unmaps a sysv shared memory segment.
|
||||
func (mm *MemoryManager) DetachShm(ctx context.Context, addr usermem.Addr) error {
|
||||
if addr != addr.RoundDown() {
|
||||
// "... shmaddr is not aligned on a page boundary." - man shmdt(2)
|
||||
return syserror.EINVAL
|
||||
}
|
||||
|
||||
var detached *shm.Shm
|
||||
mm.mappingMu.Lock()
|
||||
defer mm.mappingMu.Unlock()
|
||||
|
||||
// Find and remove the first vma containing an address >= addr that maps a
|
||||
// segment originally attached at addr.
|
||||
vseg := mm.vmas.LowerBoundSegment(addr)
|
||||
for vseg.Ok() {
|
||||
vma := vseg.ValuePtr()
|
||||
if shm, ok := vma.mappable.(*shm.Shm); ok && vseg.Start() >= addr && uint64(vseg.Start()-addr) == vma.off {
|
||||
detached = shm
|
||||
vseg = mm.unmapLocked(ctx, vseg.Range()).NextSegment()
|
||||
break
|
||||
} else {
|
||||
vseg = vseg.NextSegment()
|
||||
}
|
||||
}
|
||||
|
||||
if detached == nil {
|
||||
// There is no shared memory segment attached at addr.
|
||||
return syserror.EINVAL
|
||||
}
|
||||
|
||||
// Remove all vmas that could have been created by the same attach.
|
||||
end := addr + usermem.Addr(detached.EffectiveSize())
|
||||
for vseg.Ok() && vseg.End() <= end {
|
||||
vma := vseg.ValuePtr()
|
||||
if vma.mappable == detached && uint64(vseg.Start()-addr) == vma.off {
|
||||
vseg = mm.unmapLocked(ctx, vseg.Range()).NextSegment()
|
||||
} else {
|
||||
vseg = vseg.NextSegment()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -44,6 +44,7 @@ go_library(
|
||||
"sys_rusage.go",
|
||||
"sys_sched.go",
|
||||
"sys_sem.go",
|
||||
"sys_shm.go",
|
||||
"sys_signal.go",
|
||||
"sys_socket.go",
|
||||
"sys_stat.go",
|
||||
@@ -84,6 +85,7 @@ go_library(
|
||||
"//pkg/sentry/kernel/pipe",
|
||||
"//pkg/sentry/kernel/sched",
|
||||
"//pkg/sentry/kernel/semaphore",
|
||||
"//pkg/sentry/kernel/shm",
|
||||
"//pkg/sentry/kernel/time",
|
||||
"//pkg/sentry/limits",
|
||||
"//pkg/sentry/memmap",
|
||||
|
||||
@@ -75,9 +75,9 @@ var AMD64 = &kernel.SyscallTable{
|
||||
26: Msync,
|
||||
27: Mincore,
|
||||
28: Madvise,
|
||||
// 29: Shmget, TODO
|
||||
// 30: Shmat, TODO
|
||||
// 31: Shmctl, TODO
|
||||
29: Shmget,
|
||||
30: Shmat,
|
||||
31: Shmctl,
|
||||
32: Dup,
|
||||
33: Dup2,
|
||||
34: Pause,
|
||||
@@ -113,7 +113,7 @@ var AMD64 = &kernel.SyscallTable{
|
||||
64: Semget,
|
||||
65: Semop,
|
||||
66: Semctl,
|
||||
// 67: Shmdt, TODO
|
||||
67: Shmdt,
|
||||
// 68: Msgget, TODO
|
||||
// 69: Msgsnd, TODO
|
||||
// 70: Msgrcv, TODO
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
// Copyright 2018 Google Inc.
|
||||
//
|
||||
// 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 (
|
||||
"gvisor.googlesource.com/gvisor/pkg/abi/linux"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel"
|
||||
"gvisor.googlesource.com/gvisor/pkg/sentry/kernel/shm"
|
||||
"gvisor.googlesource.com/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
// Shmget implements shmget(2).
|
||||
func Shmget(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
key := args[0].Int()
|
||||
size := uint64(args[1].SizeT())
|
||||
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)
|
||||
|
||||
pid := int32(t.ThreadGroup().ID())
|
||||
r := t.IPCNamespace().ShmRegistry()
|
||||
segment, err := r.FindOrCreate(t, pid, key, size, mode, private, create, exclusive)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return uintptr(segment.ID), nil, nil
|
||||
}
|
||||
|
||||
// findSegment retrives a shm segment by the given id.
|
||||
func findSegment(t *kernel.Task, id int32) (*shm.Shm, error) {
|
||||
r := t.IPCNamespace().ShmRegistry()
|
||||
segment := r.FindByID(id)
|
||||
if segment == nil {
|
||||
// No segment with provided id.
|
||||
return nil, syserror.EINVAL
|
||||
}
|
||||
return segment, nil
|
||||
}
|
||||
|
||||
// Shmat implements shmat(2).
|
||||
func Shmat(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
id := args[0].Int()
|
||||
addr := args[1].Pointer()
|
||||
flag := args[2].Int()
|
||||
|
||||
segment, err := findSegment(t, id)
|
||||
if err != nil {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
|
||||
opts, err := segment.ConfigureAttach(t, addr, shm.AttachOpts{
|
||||
Execute: flag&linux.SHM_EXEC == linux.SHM_EXEC,
|
||||
Readonly: flag&linux.SHM_RDONLY == linux.SHM_RDONLY,
|
||||
Remap: flag&linux.SHM_REMAP == linux.SHM_REMAP,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
defer segment.DecRef()
|
||||
addr, err = t.MemoryManager().MMap(t, opts)
|
||||
return uintptr(addr), nil, err
|
||||
}
|
||||
|
||||
// Shmdt implements shmdt(2).
|
||||
func Shmdt(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
addr := args[0].Pointer()
|
||||
err := t.MemoryManager().DetachShm(t, addr)
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// Shmctl implements shmctl(2).
|
||||
func Shmctl(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.SyscallControl, error) {
|
||||
id := args[0].Int()
|
||||
cmd := args[1].Int()
|
||||
buf := args[2].Pointer()
|
||||
|
||||
r := t.IPCNamespace().ShmRegistry()
|
||||
|
||||
switch cmd {
|
||||
case linux.SHM_STAT:
|
||||
// Technically, we should be treating id as "an index into the kernel's
|
||||
// internal array that maintains information about all shared memory
|
||||
// segments on the system". Since we don't track segments in an array,
|
||||
// we'll just pretend the shmid is the index and do the same thing as
|
||||
// IPC_STAT. Linux also uses the index as the shmid.
|
||||
fallthrough
|
||||
case linux.IPC_STAT:
|
||||
segment, err := findSegment(t, id)
|
||||
if err != nil {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
|
||||
stat, err := segment.IPCStat(t)
|
||||
if err == nil {
|
||||
_, err = t.CopyOut(buf, stat)
|
||||
}
|
||||
return 0, nil, err
|
||||
|
||||
case linux.IPC_INFO:
|
||||
params := r.IPCInfo()
|
||||
_, err := t.CopyOut(buf, params)
|
||||
return 0, nil, err
|
||||
|
||||
case linux.SHM_INFO:
|
||||
info := r.ShmInfo()
|
||||
_, err := t.CopyOut(buf, info)
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
// Remaining commands refer to a specific segment.
|
||||
segment, err := findSegment(t, id)
|
||||
if err != nil {
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case linux.IPC_SET:
|
||||
var ds linux.ShmidDS
|
||||
_, err = t.CopyIn(buf, &ds)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
err = segment.Set(t, &ds)
|
||||
return 0, nil, err
|
||||
|
||||
case linux.IPC_RMID:
|
||||
segment.MarkDestroyed()
|
||||
return 0, nil, nil
|
||||
|
||||
case linux.SHM_LOCK, linux.SHM_UNLOCK:
|
||||
// We currently do not support memmory locking anywhere.
|
||||
// mlock(2)/munlock(2) are currently stubbed out as no-ops so do the
|
||||
// same here.
|
||||
return 0, nil, nil
|
||||
|
||||
default:
|
||||
return 0, nil, syserror.EINVAL
|
||||
}
|
||||
}
|
||||
@@ -146,7 +146,7 @@ func New(spec *specs.Spec, conf *Config, controllerFD int, ioFDs []int, console
|
||||
// not configurable from runtime spec.
|
||||
utsns := kernel.NewUTSNamespace(spec.Hostname, "", creds.UserNamespace)
|
||||
|
||||
ipcns := kernel.NewIPCNamespace()
|
||||
ipcns := kernel.NewIPCNamespace(creds.UserNamespace)
|
||||
|
||||
if err := enableStrace(conf); err != nil {
|
||||
return nil, fmt.Errorf("failed to enable strace: %v", err)
|
||||
|
||||
Reference in New Issue
Block a user