diff options
author | Andrei Vagin <avagin@google.com> | 2021-02-09 10:34:49 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-02-09 10:37:04 -0800 |
commit | fe4f4789601ddf61260271f7e1d33ba0f2756fcd (patch) | |
tree | b0d095cfb8246424bf2c2a54fa7ee4cda6914bd1 | |
parent | d6dbe6e5ca5445dac278d3b6654af8d13379878a (diff) |
kernel: reparentLocked has to update children maps of old and new parents
Reported-by: syzbot+9ffc71246fe72c73fc25@syzkaller.appspotmail.com
PiperOrigin-RevId: 356536113
-rw-r--r-- | pkg/sentry/kernel/task_exit.go | 6 | ||||
-rw-r--r-- | test/syscalls/BUILD | 4 | ||||
-rw-r--r-- | test/syscalls/linux/BUILD | 12 | ||||
-rw-r--r-- | test/syscalls/linux/processes.cc | 90 |
4 files changed, 112 insertions, 0 deletions
diff --git a/pkg/sentry/kernel/task_exit.go b/pkg/sentry/kernel/task_exit.go index 16986244c..f7765fa3a 100644 --- a/pkg/sentry/kernel/task_exit.go +++ b/pkg/sentry/kernel/task_exit.go @@ -415,6 +415,12 @@ func (tg *ThreadGroup) anyNonExitingTaskLocked() *Task { func (t *Task) reparentLocked(parent *Task) { oldParent := t.parent t.parent = parent + if oldParent != nil { + delete(oldParent.children, t) + } + if parent != nil { + parent.children[t] = struct{}{} + } // If a thread group leader's parent changes, reset the thread group's // termination signal to SIGCHLD and re-check exit notification. (Compare // kernel/exit.c:reparent_leader().) diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index e43f30ba3..d6658898d 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -993,3 +993,7 @@ syscall_test( syscall_test( test = "//test/syscalls/linux:proc_net_udp_test", ) + +syscall_test( + test = "//test/syscalls/linux:processes_test", +) diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 80e2837f8..499027e7c 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -4160,6 +4160,18 @@ cc_binary( ) cc_binary( + name = "processes_test", + testonly = 1, + srcs = ["processes.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + "//test/util:test_main", + "//test/util:test_util", + ], +) + +cc_binary( name = "xattr_test", testonly = 1, srcs = [ diff --git a/test/syscalls/linux/processes.cc b/test/syscalls/linux/processes.cc new file mode 100644 index 000000000..412582515 --- /dev/null +++ b/test/syscalls/linux/processes.cc @@ -0,0 +1,90 @@ +// Copyright 2021 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 <stdint.h> +#include <sys/syscall.h> +#include <unistd.h> + +#include "test/util/capability_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +int testSetPGIDOfZombie(void* arg) { + int p[2]; + + TEST_PCHECK(pipe(p) == 0); + + pid_t pid = fork(); + if (pid == 0) { + pid = fork(); + // Create a second child to repeat one of syzkaller reproducers. + if (pid == 0) { + pid = getpid(); + TEST_PCHECK(setpgid(pid, 0) == 0); + TEST_PCHECK(write(p[1], &pid, sizeof(pid)) == sizeof(pid)); + _exit(0); + } + TEST_PCHECK(pid > 0); + _exit(0); + } + close(p[1]); + TEST_PCHECK(pid > 0); + + // Get PID of the second child. + pid_t cpid; + TEST_PCHECK(read(p[0], &cpid, sizeof(cpid)) == sizeof(cpid)); + + // Wait when both child processes will die. + int c; + TEST_PCHECK(read(p[0], &c, sizeof(c)) == 0); + + // Wait the second child process to collect its zombie. + int status; + TEST_PCHECK(RetryEINTR(waitpid)(cpid, &status, 0) == cpid); + + // Set the child's group. + TEST_PCHECK(setpgid(pid, pid) == 0); + + TEST_PCHECK(RetryEINTR(waitpid)(-pid, &status, 0) == pid); + + TEST_PCHECK(status == 0); + _exit(0); +} + +TEST(Processes, SetPGIDOfZombie) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + + // Fork a test process in a new PID namespace, because it needs to manipulate + // with reparanted processes. + struct clone_arg { + // Reserve some space for clone() to locate arguments and retcode in this + // place. + char stack[128] __attribute__((aligned(16))); + char stack_ptr[0]; + } ca; + pid_t pid; + ASSERT_THAT(pid = clone(testSetPGIDOfZombie, ca.stack_ptr, + CLONE_NEWPID | SIGCHLD, &ca), + SyscallSucceeds()); + + int status; + ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), + SyscallSucceedsWithValue(pid)); + EXPECT_EQ(status, 0); +} + +} // namespace testing +} // namespace gvisor |