diff --git a/pkg/sentry/fsimpl/cgroupfs/base.go b/pkg/sentry/fsimpl/cgroupfs/base.go index f70734d7d..c3209573d 100644 --- a/pkg/sentry/fsimpl/cgroupfs/base.go +++ b/pkg/sentry/fsimpl/cgroupfs/base.go @@ -363,7 +363,13 @@ func (d *cgroupProcsData) Write(ctx context.Context, fd *vfs.FileDescription, sr t := kernel.TaskFromContext(ctx) currPidns := t.ThreadGroup().PIDNamespace() - targetTG := currPidns.ThreadGroupWithID(kernel.ThreadID(tgid)) + var targetTG *kernel.ThreadGroup + if tgid != 0 { + targetTG = currPidns.ThreadGroupWithID(kernel.ThreadID(tgid)) + } else { + targetTG = t.ThreadGroup() + } + if targetTG == nil { return 0, linuxerr.EINVAL } @@ -405,7 +411,12 @@ func (d *tasksData) Write(ctx context.Context, fd *vfs.FileDescription, src user t := kernel.TaskFromContext(ctx) currPidns := t.ThreadGroup().PIDNamespace() - targetTask := currPidns.TaskWithID(kernel.ThreadID(tid)) + var targetTask *kernel.Task + if tid != 0 { + targetTask = currPidns.TaskWithID(kernel.ThreadID(tid)) + } else { + targetTask = t + } if targetTask == nil { return 0, linuxerr.EINVAL } diff --git a/test/syscalls/linux/cgroup.cc b/test/syscalls/linux/cgroup.cc index 812c10c94..fc34bcd11 100644 --- a/test/syscalls/linux/cgroup.cc +++ b/test/syscalls/linux/cgroup.cc @@ -549,6 +549,42 @@ TEST(Cgroup, Rename) { EXPECT_THAT(Exists(child.Relpath("oldname")), IsPosixErrorOkAndHolds(false)); } +TEST(Cgroup, PIDZeroMovesSelf) { + SKIP_IF(!CgroupsAvailable()); + Mounter m(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir())); + Cgroup c = ASSERT_NO_ERRNO_AND_VALUE(m.MountCgroupfs("")); + Cgroup child = ASSERT_NO_ERRNO_AND_VALUE(c.CreateChild("child")); + + // Source contains this process. + EXPECT_NO_ERRNO(c.ContainsCallingProcess()); + + // Move to child by writing PID 0. + ASSERT_NO_ERRNO(child.WriteIntegerControlFile("cgroup.procs", 0)); + + // Destination now contains this process, and source does not. + EXPECT_NO_ERRNO(child.ContainsCallingProcess()); + auto procs = ASSERT_NO_ERRNO_AND_VALUE(c.Procs()); + EXPECT_FALSE(procs.contains(getpid())); +} + +TEST(Cgroup, TIDZeroMovesSelf) { + SKIP_IF(!CgroupsAvailable()); + Mounter m(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir())); + Cgroup c = ASSERT_NO_ERRNO_AND_VALUE(m.MountCgroupfs("")); + Cgroup child = ASSERT_NO_ERRNO_AND_VALUE(c.CreateChild("child")); + + // Source contains this thread. + EXPECT_NO_ERRNO(c.ContainsCallingThread()); + + // Move to child by writing TID 0. + ASSERT_NO_ERRNO(child.WriteIntegerControlFile("tasks", 0)); + + // Destination now contains this thread, and source does not. + EXPECT_NO_ERRNO(child.ContainsCallingThread()); + auto tasks = ASSERT_NO_ERRNO_AND_VALUE(c.Tasks()); + EXPECT_FALSE(tasks.contains(syscall(SYS_gettid))); +} + TEST(MemoryCgroup, MemoryUsageInBytes) { SKIP_IF(!CgroupsAvailable());