mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Implement setns CLONE_NEWUTS namespace type.
PiperOrigin-RevId: 554306089
This commit is contained in:
@@ -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}),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user