Implement setns CLONE_NEWUTS namespace type.

PiperOrigin-RevId: 554306089
This commit is contained in:
Jing Chen
2023-08-06 15:33:25 -07:00
committed by gVisor bot
parent 8f6af3062d
commit e89e40fded
10 changed files with 124 additions and 5 deletions
+1
View File
@@ -77,6 +77,7 @@ func (fs *filesystem) newTaskInode(ctx context.Context, task *kernel.Task, pidns
"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),
"uts": fs.newNamespaceSymlink(ctx, task, fs.NextIno(), linux.CLONE_NEWUTS),
}),
"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
@@ -1283,6 +1283,11 @@ func (s *namespaceSymlink) getInode(t *kernel.Task) *nsfs.Inode {
return ipcns.GetInode()
}
return nil
case linux.CLONE_NEWUTS:
if utsns := t.GetUTSNamespace(); utsns != nil {
return utsns.GetInode()
}
return nil
case linux.CLONE_NEWNS:
mntns := t.GetMountNamespace()
if mntns == nil {
+1
View File
@@ -169,6 +169,7 @@ var _ dynamicInode = (*hostnameData)(nil)
// Generate implements vfs.DynamicBytesSource.Generate.
func (*hostnameData) Generate(ctx context.Context, buf *bytes.Buffer) error {
utsns := kernel.UTSNamespaceFromContext(ctx)
defer utsns.DecRef(ctx)
buf.WriteString(utsns.HostName())
buf.WriteString("\n")
return nil
+8 -2
View File
@@ -452,6 +452,7 @@ func (k *Kernel) Init(args InitKernelArgs) error {
k.nsfsMount = k.vfs.NewDisconnectedMount(nsfsFilesystem, nil, &vfs.MountOptions{})
k.rootNetworkNamespace.SetInode(nsfs.NewInode(ctx, k.nsfsMount, k.rootNetworkNamespace))
k.rootIPCNamespace.SetInode(nsfs.NewInode(ctx, k.nsfsMount, k.rootIPCNamespace))
k.rootUTSNamespace.SetInode(nsfs.NewInode(ctx, k.nsfsMount, k.rootUTSNamespace))
tmpfsOpts := vfs.GetFilesystemOptions{
InternalData: tmpfs.FilesystemOpts{
@@ -769,7 +770,9 @@ func (ctx *createProcessContext) Value(key any) any {
case CtxPIDNamespace:
return ctx.args.PIDNamespace
case CtxUTSNamespace:
return ctx.args.UTSNamespace
utsns := ctx.args.UTSNamespace
utsns.IncRef()
return utsns
case ipc.CtxIPCNamespace:
ipcns := ctx.args.IPCNamespace
ipcns.IncRef()
@@ -1284,6 +1287,7 @@ func (k *Kernel) RootUserNamespace() *auth.UserNamespace {
// RootUTSNamespace returns the root UTSNamespace.
func (k *Kernel) RootUTSNamespace() *UTSNamespace {
k.rootUTSNamespace.IncRef()
return k.rootUTSNamespace
}
@@ -1527,7 +1531,9 @@ func (ctx *supervisorContext) Value(key any) any {
case CtxPIDNamespace:
return ctx.Kernel.tasks.Root
case CtxUTSNamespace:
return ctx.Kernel.rootUTSNamespace
utsns := ctx.Kernel.rootUTSNamespace
utsns.IncRef()
return utsns
case ipc.CtxIPCNamespace:
ipcns := ctx.Kernel.rootIPCNamespace
ipcns.IncRef()
+26 -2
View File
@@ -107,12 +107,18 @@ func (t *Task) Clone(args *linux.CloneArgs) (ThreadID, *SyscallControl, error) {
cu := cleanup.Make(func() {})
defer cu.Clean()
utsns := t.UTSNamespace()
utsns := t.utsns
if args.Flags&linux.CLONE_NEWUTS != 0 {
// Note that this must happen after NewUserNamespace so we get
// the new userns if there is one.
utsns = t.UTSNamespace().Clone(userns)
utsns = utsns.Clone(userns)
utsns.SetInode(nsfs.NewInode(t, t.k.nsfsMount, utsns))
} else {
utsns.IncRef()
}
cu.Add(func() {
utsns.DecRef(t)
})
ipcns := t.ipcns
if args.Flags&linux.CLONE_NEWIPC != 0 {
@@ -495,6 +501,21 @@ func (t *Task) Setns(fd *vfs.FileDescription, flags int32) error {
oldNS.DecRef(t)
oldFSContext.DecRef(t)
return nil
case *UTSNamespace:
if flags != 0 && flags != linux.CLONE_NEWUTS {
return linuxerr.EINVAL
}
if !t.HasCapabilityIn(linux.CAP_SYS_ADMIN, ns.UserNamespace()) ||
!t.Credentials().HasCapability(linux.CAP_SYS_ADMIN) {
return linuxerr.EPERM
}
oldNS := t.UTSNamespace()
ns.IncRef()
t.mu.Lock()
t.utsns = ns
t.mu.Unlock()
oldNS.DecRef(t)
return nil
default:
return linuxerr.EINVAL
}
@@ -588,7 +609,10 @@ func (t *Task) Unshare(flags int32) error {
}
// Note that this must happen after NewUserNamespace, so the
// new user namespace is used if there is one.
oldUTSNS := t.utsns
t.utsns = t.utsns.Clone(creds.UserNamespace)
t.utsns.SetInode(nsfs.NewInode(t, t.k.nsfsMount, t.utsns))
cu.Add(func() { oldUTSNS.DecRef(t) })
}
if flags&linux.CLONE_NEWIPC != 0 {
if !haveCapSysAdmin {
+3 -1
View File
@@ -73,7 +73,9 @@ func (t *Task) contextValue(key any, isTaskGoroutine bool) any {
t.mu.Lock()
defer t.mu.Unlock()
}
return t.utsns
utsns := t.utsns
utsns.IncRef()
return utsns
case ipc.CtxIPCNamespace:
if !isTaskGoroutine {
t.mu.Lock()
+3
View File
@@ -287,12 +287,15 @@ func (*runExitMain) execute(t *Task) taskRunState {
t.mu.Lock()
mntns := t.mountNamespace
t.mountNamespace = nil
utsns := t.utsns
t.utsns = nil
ipcns := t.ipcns
t.ipcns = nil
netns := t.netns
t.netns = nil
t.mu.Unlock()
mntns.DecRef(t)
utsns.DecRef(t)
ipcns.DecRef(t)
netns.DecRef(t)
+1
View File
@@ -116,6 +116,7 @@ func (ts *TaskSet) NewTask(ctx context.Context, cfg *TaskConfig) (*Task, error)
cfg.TaskImage.release(ctx)
cfg.FSContext.DecRef(ctx)
cfg.FDTable.DecRef(ctx)
cfg.UTSNamespace.DecRef(ctx)
cfg.IPCNamespace.DecRef(ctx)
cfg.NetworkNamespace.DecRef(ctx)
if cfg.MountNamespace != nil {
+51
View File
@@ -15,6 +15,8 @@
package kernel
import (
"gvisor.dev/gvisor/pkg/context"
"gvisor.dev/gvisor/pkg/sentry/fsimpl/nsfs"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sync"
)
@@ -35,6 +37,8 @@ type UTSNamespace struct {
//
// userns is immutable.
userns *auth.UserNamespace
inode *nsfs.Inode
}
// NewUTSNamespace creates a new UTS namespace.
@@ -53,6 +57,17 @@ func (t *Task) UTSNamespace() *UTSNamespace {
return t.utsns
}
// GetUTSNamespace takes a reference on the task UTS namespace and
// returns it. It will return nil if the task isn't alive.
func (t *Task) GetUTSNamespace() *UTSNamespace {
t.mu.Lock()
defer t.mu.Unlock()
if t.utsns != nil {
t.utsns.IncRef()
}
return t.utsns
}
// HostName returns the host name of this UTS namespace.
func (u *UTSNamespace) HostName() string {
u.mu.Lock()
@@ -88,6 +103,42 @@ func (u *UTSNamespace) UserNamespace() *auth.UserNamespace {
return u.userns
}
// Type implements nsfs.Namespace.Type.
func (u *UTSNamespace) Type() string {
return "uts"
}
// Destroy implements nsfs.Namespace.Destroy.
func (u *UTSNamespace) Destroy(ctx context.Context) {}
// SetInode sets the nsfs `inode` to the UTS namespace.
func (u *UTSNamespace) SetInode(inode *nsfs.Inode) {
u.mu.Lock()
defer u.mu.Unlock()
u.inode = inode
}
// GetInode returns the nsfs inode associated with the UTS namespace.
func (u *UTSNamespace) GetInode() *nsfs.Inode {
u.mu.Lock()
defer u.mu.Unlock()
return u.inode
}
// IncRef increments the Namespace's refcount.
func (u *UTSNamespace) IncRef() {
u.mu.Lock()
defer u.mu.Unlock()
u.inode.IncRef()
}
// DecRef decrements the namespace's refcount.
func (u *UTSNamespace) DecRef(ctx context.Context) {
u.mu.Lock()
defer u.mu.Unlock()
u.inode.DecRef(ctx)
}
// Clone makes a copy of this UTS namespace, associating the given user
// namespace.
func (u *UTSNamespace) Clone(userns *auth.UserNamespace) *UTSNamespace {
+25
View File
@@ -14,6 +14,9 @@
#include <sched.h>
#include <cstdint>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "test/util/file_descriptor.h"
#include "test/util/linux_capability_util.h"
@@ -46,6 +49,28 @@ TEST(SetnsTest, ChangeIPCNamespace) {
EXPECT_EQ(ipcns1, ipcns3);
}
TEST(SetnsTest, ChangeUTSNamespace) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
struct stat st;
uint64_t utsns1, utsns2, utsns3;
const FileDescriptor nsfd =
ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/thread-self/ns/uts", O_RDONLY));
ASSERT_THAT(stat("/proc/thread-self/ns/uts", &st), SyscallSucceeds());
utsns1 = st.st_ino;
// Use unshare(CLONE_NEWUTS) to change into a new UTS namespace.
ASSERT_THAT(unshare(CLONE_NEWUTS), SyscallSucceedsWithValue(0));
ASSERT_THAT(stat("/proc/thread-self/ns/uts", &st), SyscallSucceeds());
utsns2 = st.st_ino;
ASSERT_NE(utsns1, utsns2);
ASSERT_THAT(setns(nsfd.get(), CLONE_NEWUTS), SyscallSucceedsWithValue(0));
ASSERT_THAT(stat("/proc/thread-self/ns/uts", &st), SyscallSucceeds());
utsns3 = st.st_ino;
EXPECT_EQ(utsns1, utsns3);
}
} // namespace
} // namespace testing
} // namespace gvisor