From 3657484eee1290672f10b251ac32b2f0a634483b Mon Sep 17 00:00:00 2001 From: Shambhavi Srivastava Date: Thu, 17 Aug 2023 11:39:07 -0700 Subject: [PATCH] Adding /proc/[pid]/task/[tid]/children PiperOrigin-RevId: 557888186 --- pkg/sentry/fsimpl/proc/task.go | 2 ++ pkg/sentry/fsimpl/proc/task_files.go | 34 ++++++++++++++++++++++++++++ pkg/sentry/kernel/threads.go | 13 +++++++++++ test/syscalls/linux/proc.cc | 32 ++++++++++++++++++++++++++ 4 files changed, 81 insertions(+) diff --git a/pkg/sentry/fsimpl/proc/task.go b/pkg/sentry/fsimpl/proc/task.go index c15c23ca0..8e03c09a4 100644 --- a/pkg/sentry/fsimpl/proc/task.go +++ b/pkg/sentry/fsimpl/proc/task.go @@ -90,6 +90,8 @@ func (fs *filesystem) newTaskInode(ctx context.Context, task *kernel.Task, pidns } if isThreadGroup { contents["task"] = fs.newSubtasks(ctx, task, pidns, fakeCgroupControllers) + } else { + contents["children"] = fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0644, &childrenData{task: task, pidns: pidns}) } if len(fakeCgroupControllers) > 0 { contents["cgroup"] = fs.newTaskOwnedInode(ctx, task, fs.NextIno(), 0444, newFakeCgroupData(fakeCgroupControllers)) diff --git a/pkg/sentry/fsimpl/proc/task_files.go b/pkg/sentry/fsimpl/proc/task_files.go index 06a7cf013..c9047d4e0 100644 --- a/pkg/sentry/fsimpl/proc/task_files.go +++ b/pkg/sentry/fsimpl/proc/task_files.go @@ -18,6 +18,7 @@ import ( "bytes" "fmt" "io" + "sort" "strconv" "strings" @@ -1440,3 +1441,36 @@ func (d *taskCgroupData) Generate(ctx context.Context, buf *bytes.Buffer) error d.task.GenerateProcTaskCgroup(buf) return nil } + +// childrenData implements vfs.DynamicBytesSource for /proc/[pid]/task/[tid]/children. +// +// +stateify savable +type childrenData struct { + kernfs.DynamicBytesFile + + task *kernel.Task + + // pidns is the PID namespace associated with the proc filesystem that + // includes the file using this childrenData. + pidns *kernel.PIDNamespace +} + +// Generate implements vfs.DynamicBytesSource.Generate. +func (d *childrenData) Generate(ctx context.Context, buf *bytes.Buffer) error { + children := d.task.Children() + var childrenTIDs []int + for childTask := range children { + childrenTIDs = append(childrenTIDs, int(d.pidns.IDOfTask(childTask))) + } + + // The TIDs need to be in sorted order in accordance with the Linux implementation. + sort.Ints(childrenTIDs) + + for _, childrenTID := range childrenTIDs { + // It contains a space-separated list of child tasks of the `task`. + // Each task is represented by its TID. + fmt.Fprintf(buf, "%d ", childrenTID) + } + + return nil +} diff --git a/pkg/sentry/kernel/threads.go b/pkg/sentry/kernel/threads.go index e54da8ef9..a0b2cbc38 100644 --- a/pkg/sentry/kernel/threads.go +++ b/pkg/sentry/kernel/threads.go @@ -545,3 +545,16 @@ func (t *Task) ThreadID() ThreadID { func (t *Task) TGIDInRoot() ThreadID { return t.tg.pidns.owner.Root.IDOfThreadGroup(t.tg) } + +// Children returns children of this task. +func (t *Task) Children() map[*Task]struct{} { + t.tg.pidns.owner.mu.RLock() + defer t.tg.pidns.owner.mu.RUnlock() + + children := make(map[*Task]struct{}, len(t.children)) + for child, val := range t.children { + children[child] = val + } + + return children +} diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc index 78edcf186..3afc6126d 100644 --- a/test/syscalls/linux/proc.cc +++ b/test/syscalls/linux/proc.cc @@ -2390,6 +2390,38 @@ TEST(ProcTask, VerifyTaskDir) { DirContains(absl::StrCat("/proc/self/task/", getpid()), {}, {"task"})); } +TEST(ProcTask, VerifyTaskChildren) { + auto path = JoinPath("/proc", absl::StrCat(getpid()), "task", + absl::StrCat(gettid()), "children"); + EXPECT_THAT(access(path.c_str(), F_OK), SyscallSucceeds()); + + int pid1 = -1, status1 = -1; + auto cleanup1 = + ForkAndExec("/bin/sleep", {"sleep", "100"}, {}, nullptr, &pid1, &status1); + ASSERT_GT(pid1, 0); + ASSERT_EQ(status1, 0); + + auto proc_children_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents(path)); + EXPECT_EQ(absl::StrCat(pid1, " "), proc_children_file); + + int pid2 = -1, status2 = -1; + auto cleanup2 = + ForkAndExec("/bin/sleep", {"sleep", "100"}, {}, nullptr, &pid2, &status2); + ASSERT_GT(pid2, 0); + ASSERT_EQ(status2, 0); + + proc_children_file = ASSERT_NO_ERRNO_AND_VALUE(GetContents(path)); + + // /children contains space-separated sorted list of thread Ids of children. + std::string expectedContent; + if (pid1 < pid2) { + expectedContent = absl::StrCat(pid1, " ", pid2, " "); + } else { + expectedContent = absl::StrCat(pid2, " ", pid1, " "); + } + EXPECT_EQ(expectedContent, proc_children_file); +} + TEST(ProcTask, TaskDirCannotBeDeleted) { // Drop capabilities that allow us to override file and directory permissions. AutoCapability cap(CAP_DAC_OVERRIDE, false);