mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement setns CLONE_NEWIPC namespace type.
PiperOrigin-RevId: 552619565
This commit is contained in:
@@ -75,6 +75,7 @@ func (fs *filesystem) newTaskInode(ctx context.Context, task *kernel.Task, pidns
|
||||
"net": fs.newNamespaceSymlink(ctx, task, fs.NextIno(), linux.CLONE_NEWNET),
|
||||
"pid": fs.newPIDNamespaceSymlink(ctx, task, fs.NextIno()),
|
||||
"user": fs.newFakeNamespaceSymlink(ctx, task, fs.NextIno(), "user"),
|
||||
"ipc": fs.newNamespaceSymlink(ctx, task, fs.NextIno(), linux.CLONE_NEWIPC),
|
||||
}),
|
||||
"oom_score": fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0444, newStaticFile("0\n")),
|
||||
"oom_score_adj": fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0644, &oomScoreAdj{task: task}),
|
||||
|
||||
@@ -1274,6 +1274,11 @@ func (s *namespaceSymlink) getInode(t *kernel.Task) *nsfs.Inode {
|
||||
switch s.nsType {
|
||||
case linux.CLONE_NEWNET:
|
||||
return t.GetNetworkNamespace().GetInode()
|
||||
case linux.CLONE_NEWIPC:
|
||||
if ipcns := t.GetIPCNamespace(); ipcns != nil {
|
||||
return ipcns.GetInode()
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
panic("unknown namespace")
|
||||
}
|
||||
|
||||
@@ -193,17 +193,6 @@ go_template_instance(
|
||||
},
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "ipc_namespace_refs",
|
||||
out = "ipc_namespace_refs.go",
|
||||
package = "kernel",
|
||||
prefix = "IPCNamespace",
|
||||
template = "//pkg/refs:refs_template",
|
||||
types = {
|
||||
"T": "IPCNamespace",
|
||||
},
|
||||
)
|
||||
|
||||
go_template_instance(
|
||||
name = "process_group_refs",
|
||||
out = "process_group_refs.go",
|
||||
@@ -252,7 +241,6 @@ go_library(
|
||||
"fs_context.go",
|
||||
"fs_context_refs.go",
|
||||
"ipc_namespace.go",
|
||||
"ipc_namespace_refs.go",
|
||||
"kcov.go",
|
||||
"kcov_unsafe.go",
|
||||
"kernel.go",
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/mqfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/mq"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/msgqueue"
|
||||
@@ -31,7 +32,7 @@ import (
|
||||
//
|
||||
// +stateify savable
|
||||
type IPCNamespace struct {
|
||||
IPCNamespaceRefs
|
||||
inode *nsfs.Inode
|
||||
|
||||
// User namespace which owns this IPC namespace. Immutable.
|
||||
userNS *auth.UserNamespace
|
||||
@@ -57,10 +58,37 @@ func NewIPCNamespace(userNS *auth.UserNamespace) *IPCNamespace {
|
||||
semaphores: semaphore.NewRegistry(userNS),
|
||||
shms: shm.NewRegistry(userNS),
|
||||
}
|
||||
ns.InitRefs()
|
||||
return ns
|
||||
}
|
||||
|
||||
// Type implements nsfs.Namespace.Type.
|
||||
func (i *IPCNamespace) Type() string {
|
||||
return "ipc"
|
||||
}
|
||||
|
||||
// Destroy implements nsfs.Namespace.Destroy.
|
||||
func (i *IPCNamespace) Destroy(ctx context.Context) {
|
||||
i.shms.Release(ctx)
|
||||
if i.posixQueues != nil {
|
||||
i.posixQueues.Destroy(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// SetInode sets the nsfs `inode` to the IPC namespace.
|
||||
func (i *IPCNamespace) SetInode(inode *nsfs.Inode) {
|
||||
i.inode = inode
|
||||
}
|
||||
|
||||
// GetInode returns the nsfs inode associated with the IPC namespace.
|
||||
func (i *IPCNamespace) GetInode() *nsfs.Inode {
|
||||
return i.inode
|
||||
}
|
||||
|
||||
// UserNamespace returns the user namespace associated with the namespace.
|
||||
func (i *IPCNamespace) UserNamespace() *auth.UserNamespace {
|
||||
return i.userNS
|
||||
}
|
||||
|
||||
// MsgqueueRegistry returns the message queue registry for this namespace.
|
||||
func (i *IPCNamespace) MsgqueueRegistry() *msgqueue.Registry {
|
||||
return i.queues
|
||||
@@ -98,14 +126,14 @@ func (i *IPCNamespace) PosixQueues() *mq.Registry {
|
||||
return i.posixQueues
|
||||
}
|
||||
|
||||
// DecRef implements refs.RefCounter.DecRef.
|
||||
// IncRef increments the Namespace's refcount.
|
||||
func (i *IPCNamespace) IncRef() {
|
||||
i.inode.IncRef()
|
||||
}
|
||||
|
||||
// DecRef decrements the namespace's refcount.
|
||||
func (i *IPCNamespace) DecRef(ctx context.Context) {
|
||||
i.IPCNamespaceRefs.DecRef(func() {
|
||||
i.shms.Release(ctx)
|
||||
if i.posixQueues != nil {
|
||||
i.posixQueues.Destroy(ctx)
|
||||
}
|
||||
})
|
||||
i.inode.DecRef(ctx)
|
||||
}
|
||||
|
||||
// IPCNamespace returns the task's IPC namespace.
|
||||
@@ -114,3 +142,14 @@ func (t *Task) IPCNamespace() *IPCNamespace {
|
||||
defer t.mu.Unlock()
|
||||
return t.ipcns
|
||||
}
|
||||
|
||||
// GetIPCNamespace takes a reference on the task IPC namespace and
|
||||
// returns it. It will return nil if the task isn't alive.
|
||||
func (t *Task) GetIPCNamespace() *IPCNamespace {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if t.ipcns != nil {
|
||||
t.ipcns.IncRef()
|
||||
}
|
||||
return t.ipcns
|
||||
}
|
||||
|
||||
@@ -451,6 +451,7 @@ func (k *Kernel) Init(args InitKernelArgs) error {
|
||||
nsfsMount := k.vfs.NewDisconnectedMount(nsfsFilesystem, nil, &vfs.MountOptions{})
|
||||
k.nsfsMount = nsfsMount
|
||||
k.rootNetworkNamespace.SetInode(nsfs.NewInode(ctx, nsfsMount, k.rootNetworkNamespace))
|
||||
k.rootIPCNamespace.SetInode(nsfs.NewInode(ctx, nsfsMount, k.rootIPCNamespace))
|
||||
|
||||
tmpfsOpts := vfs.GetFilesystemOptions{
|
||||
InternalData: tmpfs.FilesystemOpts{
|
||||
|
||||
@@ -101,6 +101,9 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) {
|
||||
return 0, nil, linuxerr.EPERM
|
||||
}
|
||||
|
||||
cu := cleanup.Make(func() {})
|
||||
defer cu.Clean()
|
||||
|
||||
utsns := t.UTSNamespace()
|
||||
if args.Flags&linux.CLONE_NEWUTS != 0 {
|
||||
// Note that this must happen after NewUserNamespace so we get
|
||||
@@ -108,17 +111,17 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) {
|
||||
utsns = t.UTSNamespace().Clone(userns)
|
||||
}
|
||||
|
||||
ipcns := t.IPCNamespace()
|
||||
ipcns := t.ipcns
|
||||
if args.Flags&linux.CLONE_NEWIPC != 0 {
|
||||
ipcns = NewIPCNamespace(userns)
|
||||
ipcns.InitPosixQueues(t, t.k.VFS(), creds)
|
||||
ipcns.SetInode(nsfs.NewInode(t, t.k.nsfsMount, ipcns))
|
||||
} else {
|
||||
ipcns.IncRef()
|
||||
}
|
||||
cu := cleanup.Make(func() {
|
||||
cu.Add(func() {
|
||||
ipcns.DecRef(t)
|
||||
})
|
||||
defer cu.Clean()
|
||||
|
||||
netns := t.netns.Load()
|
||||
if args.Flags&linux.CLONE_NEWNET != 0 {
|
||||
@@ -437,6 +440,21 @@ func (t *Task) Setns(fd *vfs.FileDescription, flags int32) error {
|
||||
t.mu.Unlock()
|
||||
oldNS.DecRef(t)
|
||||
return nil
|
||||
case *IPCNamespace:
|
||||
if flags != 0 && flags != linux.CLONE_NEWIPC {
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
if !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, ns.UserNamespace()) ||
|
||||
!t.Credentials().HasCapability(linux.CAP_SYS_ADMIN) {
|
||||
return linuxerr.EPERM
|
||||
}
|
||||
oldNS := t.IPCNamespace()
|
||||
ns.IncRef()
|
||||
t.mu.Lock()
|
||||
t.ipcns = ns
|
||||
t.mu.Unlock()
|
||||
oldNS.DecRef(t)
|
||||
return nil
|
||||
default:
|
||||
return linuxerr.EINVAL
|
||||
}
|
||||
@@ -538,6 +556,10 @@ func (t *Task) Unshare(flags int32) error {
|
||||
oldIPCNS = t.ipcns
|
||||
t.ipcns = NewIPCNamespace(creds.UserNamespace)
|
||||
t.ipcns.InitPosixQueues(t, t.k.VFS(), creds)
|
||||
t.ipcns.SetInode(nsfs.NewInode(t, t.k.nsfsMount, t.ipcns))
|
||||
if oldIPCNS != nil {
|
||||
oldIPCNS.DecRef(t)
|
||||
}
|
||||
}
|
||||
var oldFDTable *FDTable
|
||||
if flags&linux.CLONE_FILES != 0 {
|
||||
@@ -550,9 +572,6 @@ func (t *Task) Unshare(flags int32) error {
|
||||
t.fsContext = oldFSContext.Fork()
|
||||
}
|
||||
t.mu.Unlock()
|
||||
if oldIPCNS != nil {
|
||||
oldIPCNS.DecRef(t)
|
||||
}
|
||||
if oldFDTable != nil {
|
||||
oldFDTable.DecRef(t)
|
||||
}
|
||||
|
||||
@@ -668,6 +668,10 @@ syscall_test(
|
||||
use_tmpfs = True,
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
test = "//test/syscalls/linux:setns_test",
|
||||
)
|
||||
|
||||
syscall_test(
|
||||
add_overlay = True,
|
||||
test = "//test/syscalls/linux:splice_test",
|
||||
|
||||
@@ -2323,6 +2323,21 @@ cc_binary(
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "setns_test",
|
||||
testonly = 1,
|
||||
srcs = ["setns.cc"],
|
||||
linkstatic = 1,
|
||||
deps = [
|
||||
"//test/util:capability_util",
|
||||
"//test/util:file_descriptor",
|
||||
"//test/util:posix_error",
|
||||
"//test/util:test_main",
|
||||
"//test/util:test_util",
|
||||
gtest,
|
||||
],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "splice_test",
|
||||
testonly = 1,
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2023 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.
|
||||
|
||||
#include <sched.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/util/file_descriptor.h"
|
||||
#include "test/util/linux_capability_util.h"
|
||||
#include "test/util/posix_error.h"
|
||||
#include "test/util/test_util.h"
|
||||
|
||||
namespace gvisor {
|
||||
namespace testing {
|
||||
namespace {
|
||||
|
||||
TEST(SetnsTest, ChangeIPCNamespace) {
|
||||
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
|
||||
|
||||
struct stat st;
|
||||
uint64_t ipcns1, ipcns2, ipcns3;
|
||||
const FileDescriptor nsfd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/thread-self/ns/ipc", O_RDONLY));
|
||||
ASSERT_THAT(stat("/proc/thread-self/ns/ipc", &st), SyscallSucceeds());
|
||||
ipcns1 = st.st_ino;
|
||||
|
||||
// Use unshare(CLONE_NEWIPC) to change into a new IPC namespace.
|
||||
ASSERT_THAT(unshare(CLONE_NEWIPC), SyscallSucceedsWithValue(0));
|
||||
ASSERT_THAT(stat("/proc/thread-self/ns/ipc", &st), SyscallSucceeds());
|
||||
ipcns2 = st.st_ino;
|
||||
ASSERT_NE(ipcns1, ipcns2);
|
||||
|
||||
ASSERT_THAT(setns(nsfd.get(), CLONE_NEWIPC), SyscallSucceedsWithValue(0));
|
||||
ASSERT_THAT(stat("/proc/thread-self/ns/ipc", &st), SyscallSucceeds());
|
||||
ipcns3 = st.st_ino;
|
||||
EXPECT_EQ(ipcns1, ipcns3);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace testing
|
||||
} // namespace gvisor
|
||||
Reference in New Issue
Block a user