selftests/ptrace: add test cases for dead-locks
authorBernd Edlinger <bernd.edlinger@hotmail.de>
Fri, 20 Mar 2020 20:26:19 +0000 (21:26 +0100)
committerEric W. Biederman <ebiederm@xmission.com>
Wed, 25 Mar 2020 15:04:01 +0000 (10:04 -0500)
This adds test cases for ptrace deadlocks.

Additionally fixes a compile problem in get_syscall_info.c,
observed with gcc-4.8.4:

get_syscall_info.c: In function 'get_syscall_info':
get_syscall_info.c:93:3: error: 'for' loop initial declarations are only
                                 allowed in C99 mode
   for (unsigned int i = 0; i < ARRAY_SIZE(args); ++i) {
   ^
get_syscall_info.c:93:3: note: use option -std=c99 or -std=gnu99 to compile
                               your code

Signed-off-by: Bernd Edlinger <bernd.edlinger@hotmail.de>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
tools/testing/selftests/ptrace/Makefile
tools/testing/selftests/ptrace/vmaccess.c [new file with mode: 0644]

index c0b7f89..2f1f532 100644 (file)
@@ -1,6 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
-CFLAGS += -iquote../../../../include/uapi -Wall
+CFLAGS += -std=c99 -pthread -iquote../../../../include/uapi -Wall
 
-TEST_GEN_PROGS := get_syscall_info peeksiginfo
+TEST_GEN_PROGS := get_syscall_info peeksiginfo vmaccess
 
 include ../lib.mk
diff --git a/tools/testing/selftests/ptrace/vmaccess.c b/tools/testing/selftests/ptrace/vmaccess.c
new file mode 100644 (file)
index 0000000..4db327b
--- /dev/null
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020 Bernd Edlinger <bernd.edlinger@hotmail.de>
+ * All rights reserved.
+ *
+ * Check whether /proc/$pid/mem can be accessed without causing deadlocks
+ * when de_thread is blocked with ->cred_guard_mutex held.
+ */
+
+#include "../kselftest_harness.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/ptrace.h>
+
+static void *thread(void *arg)
+{
+       ptrace(PTRACE_TRACEME, 0, 0L, 0L);
+       return NULL;
+}
+
+TEST(vmaccess)
+{
+       int f, pid = fork();
+       char mm[64];
+
+       if (!pid) {
+               pthread_t pt;
+
+               pthread_create(&pt, NULL, thread, NULL);
+               pthread_join(pt, NULL);
+               execlp("true", "true", NULL);
+       }
+
+       sleep(1);
+       sprintf(mm, "/proc/%d/mem", pid);
+       f = open(mm, O_RDONLY);
+       ASSERT_GE(f, 0);
+       close(f);
+       f = kill(pid, SIGCONT);
+       ASSERT_EQ(f, 0);
+}
+
+TEST(attach)
+{
+       int s, k, pid = fork();
+
+       if (!pid) {
+               pthread_t pt;
+
+               pthread_create(&pt, NULL, thread, NULL);
+               pthread_join(pt, NULL);
+               execlp("sleep", "sleep", "2", NULL);
+       }
+
+       sleep(1);
+       k = ptrace(PTRACE_ATTACH, pid, 0L, 0L);
+       ASSERT_EQ(errno, EAGAIN);
+       ASSERT_EQ(k, -1);
+       k = waitpid(-1, &s, WNOHANG);
+       ASSERT_NE(k, -1);
+       ASSERT_NE(k, 0);
+       ASSERT_NE(k, pid);
+       ASSERT_EQ(WIFEXITED(s), 1);
+       ASSERT_EQ(WEXITSTATUS(s), 0);
+       sleep(1);
+       k = ptrace(PTRACE_ATTACH, pid, 0L, 0L);
+       ASSERT_EQ(k, 0);
+       k = waitpid(-1, &s, 0);
+       ASSERT_EQ(k, pid);
+       ASSERT_EQ(WIFSTOPPED(s), 1);
+       ASSERT_EQ(WSTOPSIG(s), SIGSTOP);
+       k = ptrace(PTRACE_DETACH, pid, 0L, 0L);
+       ASSERT_EQ(k, 0);
+       k = waitpid(-1, &s, 0);
+       ASSERT_EQ(k, pid);
+       ASSERT_EQ(WIFEXITED(s), 1);
+       ASSERT_EQ(WEXITSTATUS(s), 0);
+       k = waitpid(-1, NULL, 0);
+       ASSERT_EQ(k, -1);
+       ASSERT_EQ(errno, ECHILD);
+}
+
+TEST_HARNESS_MAIN