Implement setns CLONE_NEWIPC namespace type.

PiperOrigin-RevId: 552619565
This commit is contained in:
Jing Chen
2023-07-31 16:12:45 -07:00
committed by gVisor bot
parent 7981df85f3
commit 7f067c7e1d
9 changed files with 150 additions and 27 deletions
+1
View File
@@ -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}),
+5
View File
@@ -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")
}
-12
View File
@@ -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",
+48 -9
View File
@@ -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
}
+1
View File
@@ -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{
+25 -6
View File
@@ -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)
}
+4
View File
@@ -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",
+15
View File
@@ -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,
+51
View File
@@ -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