summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrei Vagin <avagin@google.com>2021-02-09 10:34:49 -0800
committergVisor bot <gvisor-bot@google.com>2021-02-09 10:37:04 -0800
commitfe4f4789601ddf61260271f7e1d33ba0f2756fcd (patch)
treeb0d095cfb8246424bf2c2a54fa7ee4cda6914bd1
parentd6dbe6e5ca5445dac278d3b6654af8d13379878a (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.go6
-rw-r--r--test/syscalls/BUILD4
-rw-r--r--test/syscalls/linux/BUILD12
-rw-r--r--test/syscalls/linux/processes.cc90
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