From: Adrian Reber <areber@redhat.com>
To: Christian Brauner <christian.brauner@ubuntu.com>,
Eric Biederman <ebiederm@xmission.com>,
Pavel Emelianov <xemul@virtuozzo.com>,
Jann Horn <jannh@google.com>, Oleg Nesterov <oleg@redhat.com>,
Dmitry Safonov <0x7f454c46@gmail.com>
Cc: linux-kernel@vger.kernel.org, Andrei Vagin <avagin@gmail.com>,
Mike Rapoport <rppt@linux.ibm.com>,
Radostin Stoyanov <rstoyanov1@gmail.com>,
Adrian Reber <areber@redhat.com>
Subject: [PATCH v6 2/2] selftests: add tests for clone3()
Date: Mon, 12 Aug 2019 22:09:39 +0200 [thread overview]
Message-ID: <20190812200939.23784-2-areber@redhat.com> (raw)
In-Reply-To: <20190812200939.23784-1-areber@redhat.com>
This tests clone3() with and without set_tid to see if all desired PIDs
are working as expected. The test tries to clone3() with a set_tid of
-1, 1, pid_max, a PID which is already in use and an unused PID. The
same tests are also running in PID namespace.
In addition the clone3 test (without set_tid) tries to call clone3()
with different sizes of clone_args.
Signed-off-by: Adrian Reber <areber@redhat.com>
---
tools/testing/selftests/clone3/.gitignore | 2 +
tools/testing/selftests/clone3/Makefile | 11 +
tools/testing/selftests/clone3/clone3.c | 231 ++++++++++++++++++
.../testing/selftests/clone3/clone3_set_tid.c | 161 ++++++++++++
4 files changed, 405 insertions(+)
create mode 100644 tools/testing/selftests/clone3/.gitignore
create mode 100644 tools/testing/selftests/clone3/Makefile
create mode 100644 tools/testing/selftests/clone3/clone3.c
create mode 100644 tools/testing/selftests/clone3/clone3_set_tid.c
diff --git a/tools/testing/selftests/clone3/.gitignore b/tools/testing/selftests/clone3/.gitignore
new file mode 100644
index 000000000000..c63c64a78ddf
--- /dev/null
+++ b/tools/testing/selftests/clone3/.gitignore
@@ -0,0 +1,2 @@
+clone3_set_tid
+clone3
diff --git a/tools/testing/selftests/clone3/Makefile b/tools/testing/selftests/clone3/Makefile
new file mode 100644
index 000000000000..4efcf45b995b
--- /dev/null
+++ b/tools/testing/selftests/clone3/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+uname_M := $(shell uname -m 2>/dev/null || echo not)
+ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/)
+
+CFLAGS += -I../../../../usr/include/
+
+ifeq ($(ARCH),x86_64)
+ TEST_GEN_PROGS := clone3 clone3_set_tid
+endif
+
+include ../lib.mk
diff --git a/tools/testing/selftests/clone3/clone3.c b/tools/testing/selftests/clone3/clone3.c
new file mode 100644
index 000000000000..a0f1989f8b1b
--- /dev/null
+++ b/tools/testing/selftests/clone3/clone3.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Based on Christian Brauner's clone3() example */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sched.h>
+
+#include "../kselftest.h"
+
+/*
+ * Different sizes of struct clone_args
+ */
+#define CLONE3_ARGS_SIZE_V0 64
+/* V1 includes set_tid */
+#define CLONE3_ARGS_SIZE_V1 72
+
+#define CLONE3_ARGS_NO_TEST 0
+#define CLONE3_ARGS_ALL_0 1
+#define CLONE3_ARGS_ALL_1 2
+
+static pid_t raw_clone(struct clone_args *args, size_t size)
+{
+ return syscall(__NR_clone3, args, size);
+}
+
+static int call_clone3(int flags, size_t size, int test_mode)
+{
+ struct clone_args args = {0};
+ pid_t ppid = -1;
+ pid_t pid = -1;
+ int status;
+
+ args.flags = flags;
+ args.exit_signal = SIGCHLD;
+
+ if (size == 0)
+ size = sizeof(struct clone_args);
+
+ if (test_mode == CLONE3_ARGS_ALL_0) {
+ args.flags = 0;
+ args.pidfd = 0;
+ args.child_tid = 0;
+ args.parent_tid = 0;
+ args.exit_signal = 0;
+ args.stack = 0;
+ args. stack_size = 0;
+ args.tls = 0;
+ args.set_tid = 0;
+ } else if (test_mode == CLONE3_ARGS_ALL_1) {
+ args.flags = 1;
+ args.pidfd = 1;
+ args.child_tid = 1;
+ args.parent_tid = 1;
+ args.exit_signal = 1;
+ args.stack = 1;
+ args. stack_size = 1;
+ args.tls = 1;
+ args.set_tid = 1;
+ }
+
+ pid = raw_clone(&args, size);
+ if (pid < 0) {
+ ksft_print_msg("%s - Failed to create new process\n",
+ strerror(errno));
+ return -errno;
+ }
+
+ if (pid == 0) {
+ ksft_print_msg("I am the child, my PID is %d\n", getpid());
+ _exit(EXIT_SUCCESS);
+ }
+
+ ppid = getpid();
+ ksft_print_msg("I am the parent (%d). My child's pid is %d\n",
+ ppid, pid);
+
+ (void)wait(&status);
+ if (WEXITSTATUS(status))
+ return WEXITSTATUS(status);
+
+ return 0;
+}
+
+static int test_clone3(int flags, size_t size, int expected, int test_mode)
+{
+ int ret;
+
+ ksft_print_msg("[%d] Trying clone3() with flags 0x%x (size %d)\n",
+ getpid(), flags, size);
+ ret = call_clone3(flags, size, test_mode);
+ ksft_print_msg("[%d] clone3() with flags says :%d expected %d\n",
+ getpid(), ret, expected);
+ if (ret != expected)
+ ksft_exit_fail_msg(
+ "[%d] Result (%d) is different than expected (%d)\n",
+ getpid(), ret, expected);
+ ksft_test_result_pass("[%d] Result (%d) matches expectation (%d)\n",
+ getpid(), ret, expected);
+ return 0;
+}
+int main(int argc, char *argv[])
+{
+ int ret = -1;
+ pid_t pid;
+
+ ksft_print_header();
+ ksft_set_plan(16);
+
+ /* Just a simple clone3() should return 0.*/
+ if (test_clone3(0, 0, 0, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() in a new PID NS.*/
+ if (test_clone3(CLONE_NEWPID, 0, 0, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() with CLONE3_ARGS_SIZE_V0. */
+ if (test_clone3(0, CLONE3_ARGS_SIZE_V0, 0, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() with CLONE3_ARGS_SIZE_V1. */
+ if (test_clone3(0, CLONE3_ARGS_SIZE_V0, 0, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() with CLONE3_ARGS_SIZE_V0 - 8 */
+ if (test_clone3(0, CLONE3_ARGS_SIZE_V0 - 8, -EINVAL,
+ CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() with sizeof(struct clone_args) + 8 */
+ if (test_clone3(0, sizeof(struct clone_args) + 8, -E2BIG,
+ CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() with all members set to 1 */
+ if (test_clone3(0, CLONE3_ARGS_SIZE_V0, -EINVAL, CLONE3_ARGS_ALL_1))
+ goto on_error;
+ /*
+ * Do a clone3() with sizeof(struct clone_args) + 8
+ * and all members set to 0.
+ */
+ if (test_clone3(0, sizeof(struct clone_args) + 8, -E2BIG,
+ CLONE3_ARGS_ALL_0))
+ goto on_error;
+ /*
+ * Do a clone3() with sizeof(struct clone_args) + 8
+ * and all members set to 0.
+ */
+ if (test_clone3(0, sizeof(struct clone_args) + 8, -E2BIG,
+ CLONE3_ARGS_ALL_1))
+ goto on_error;
+ /* Do a clone3() with > page size */
+ if (test_clone3(0, getpagesize() + 8, -E2BIG, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() with CLONE3_ARGS_SIZE_V0 in a new PID NS. */
+ if (test_clone3(CLONE_NEWPID, CLONE3_ARGS_SIZE_V0, 0,
+ CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() with CLONE3_ARGS_SIZE_V1 in a new PID NS. */
+ if (test_clone3(CLONE_NEWPID, CLONE3_ARGS_SIZE_V0, 0,
+ CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() with CLONE3_ARGS_SIZE_V0 - 8 in a new PID NS */
+ if (test_clone3(CLONE_NEWPID, CLONE3_ARGS_SIZE_V0 - 8, -EINVAL,
+ CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() with sizeof(struct clone_args) + 8 in a new PID NS */
+ if (test_clone3(CLONE_NEWPID, sizeof(struct clone_args) + 8, -E2BIG,
+ CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ /* Do a clone3() with > page size in a new PID NS */
+ if (test_clone3(CLONE_NEWPID, getpagesize() + 8, -E2BIG,
+ CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ ksft_print_msg("First unshare\n");
+ if (unshare(CLONE_NEWPID))
+ goto on_error;
+ /*
+ * Before clone3()ing in a new PID NS with
+ * CLONE_NEWPID a fork() is necessary.
+ */
+ if (test_clone3(CLONE_NEWPID, 0, -EINVAL, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ pid = fork();
+ if (pid < 0) {
+ ksft_print_msg("First fork() failed\n");
+ goto on_error;
+ }
+ if (pid > 0) {
+ (void)wait(NULL);
+ goto parent_out;
+ }
+ ksft_set_plan(19);
+ if (test_clone3(CLONE_NEWPID, 0, 0, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ if (test_clone3(0, 0, 0, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ ksft_print_msg("Second unshare\n");
+ if (unshare(CLONE_NEWPID))
+ goto on_error;
+ /*
+ * Before clone3()ing in a new PID NS with
+ * CLONE_NEWPID a fork() is necessary.
+ */
+ if (test_clone3(CLONE_NEWPID, 0, -EINVAL, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ pid = fork();
+ if (pid < 0) {
+ ksft_print_msg("Second fork() failed\n");
+ goto on_error;
+ }
+ if (pid > 0) {
+ (void)wait(NULL);
+ goto parent_out;
+ }
+ ksft_set_plan(21);
+ if (test_clone3(CLONE_NEWPID, 0, 0, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+ if (test_clone3(0, 0, 0, CLONE3_ARGS_NO_TEST))
+ goto on_error;
+
+parent_out:
+ ret = 0;
+on_error:
+
+ return !ret ? ksft_exit_pass() : ksft_exit_fail();
+}
diff --git a/tools/testing/selftests/clone3/clone3_set_tid.c b/tools/testing/selftests/clone3/clone3_set_tid.c
new file mode 100644
index 000000000000..f5012e84dcb3
--- /dev/null
+++ b/tools/testing/selftests/clone3/clone3_set_tid.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* Based on Christian Brauner's clone3() example */
+
+#define _GNU_SOURCE
+#include <errno.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <sched.h>
+
+#include "../kselftest.h"
+
+static pid_t raw_clone(struct clone_args *args)
+{
+ return syscall(__NR_clone3, args, sizeof(struct clone_args));
+}
+
+static int call_clone3_set_tid(int set_tid, int flags)
+{
+ struct clone_args args = {0};
+ pid_t ppid = -1;
+ pid_t pid = -1;
+ int status;
+
+ args.flags = flags;
+ args.exit_signal = SIGCHLD;
+ args.set_tid = set_tid;
+
+ pid = raw_clone(&args);
+ if (pid < 0) {
+ ksft_print_msg("%s - Failed to create new process\n",
+ strerror(errno));
+ return -errno;
+ }
+
+ if (pid == 0) {
+ ksft_print_msg("I am the child, my PID is %d (expected %d)\n",
+ getpid(), set_tid);
+ if (set_tid != getpid())
+ _exit(EXIT_FAILURE);
+ _exit(EXIT_SUCCESS);
+ }
+
+ ppid = getpid();
+ ksft_print_msg("I am the parent (%d). My child's pid is %d\n",
+ ppid, pid);
+
+ (void)wait(&status);
+ if (WEXITSTATUS(status))
+ return WEXITSTATUS(status);
+
+ return 0;
+}
+
+static int test_clone3_set_tid(int set_tid, int flags, int expected)
+{
+ int ret;
+
+ ksft_print_msg(
+ "[%d] Trying clone3() with CLONE_SET_TID to %d and 0x%x\n",
+ getpid(), set_tid, flags);
+ ret = call_clone3_set_tid(set_tid, flags);
+ ksft_print_msg(
+ "[%d] clone3() with CLONE_SET_TID %d says :%d - expected %d\n",
+ getpid(), set_tid, ret, expected);
+ if (ret != expected)
+ ksft_exit_fail_msg(
+ "[%d] Result (%d) is different than expected (%d)\n",
+ getpid(), ret, expected);
+ ksft_test_result_pass("[%d] Result (%d) matches expectation (%d)\n",
+ getpid(), ret, expected);
+ return 0;
+}
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ int pid_max = 0;
+ pid_t pid;
+ pid_t ns_pid;
+ int ret = -1;
+
+ ksft_print_header();
+ ksft_set_plan(13);
+
+ f = fopen("/proc/sys/kernel/pid_max", "r");
+ if (f == NULL)
+ ksft_exit_fail_msg(
+ "%s - Could not open /proc/sys/kernel/pid_max\n",
+ strerror(errno));
+ fscanf(f, "%d", &pid_max);
+ fclose(f);
+ ksft_print_msg("/proc/sys/kernel/pid_max %d\n", pid_max);
+
+ /* First try with an invalid PID */
+ if (test_clone3_set_tid(-1, 0, -EINVAL))
+ goto on_error;
+ if (test_clone3_set_tid(-1, CLONE_NEWPID, -EINVAL))
+ goto on_error;
+ /* Then with PID 1 */
+ if (test_clone3_set_tid(1, 0, -EEXIST))
+ goto on_error;
+ /* PID 1 should not fail in a PID namespace */
+ if (test_clone3_set_tid(1, CLONE_NEWPID, 0))
+ goto on_error;
+ /* pid_max should fail everywhere */
+ if (test_clone3_set_tid(pid_max, 0, -EINVAL))
+ goto on_error;
+ if (test_clone3_set_tid(pid_max, CLONE_NEWPID, -EINVAL))
+ goto on_error;
+ /* Find the current active PID */
+ pid = fork();
+ if (pid == 0) {
+ ksft_print_msg("Child has PID %d\n", getpid());
+ sleep(1);
+ _exit(EXIT_SUCCESS);
+ }
+ /* Try to create a process with that PID should fail */
+ if (test_clone3_set_tid(pid, 0, -EEXIST))
+ goto on_error;
+ (void)wait(NULL);
+ /* After the child has finished, try again with the same PID */
+ if (test_clone3_set_tid(pid, 0, 0))
+ goto on_error;
+ /* This should fail as there is no PID 1 in that namespace */
+ if (test_clone3_set_tid(pid, CLONE_NEWPID, -EINVAL))
+ goto on_error;
+ unshare(CLONE_NEWPID);
+ if (test_clone3_set_tid(10, 0, -EINVAL))
+ goto on_error;
+ /* Let's create a PID 1 */
+ ns_pid = fork();
+ if (ns_pid == 0) {
+ ksft_print_msg("Child in PID namespace has PID %d\n", getpid());
+ sleep(1);
+ _exit(EXIT_SUCCESS);
+ }
+ /*
+ * Now, after the unshare() it should be possible to create a process
+ * with another ID than 1 in the PID namespace.
+ */
+ if (test_clone3_set_tid(2, 0, 0))
+ goto on_error;
+ /* Use a different PID in this namespace. */
+ if (test_clone3_set_tid(2222, 0, 0))
+ goto on_error;
+ if (test_clone3_set_tid(1, 0, -EEXIST))
+ goto on_error;
+ (void)wait(NULL);
+
+ ret = 0;
+on_error:
+
+ return !ret ? ksft_exit_pass() : ksft_exit_fail();
+}
--
2.21.0
next prev parent reply other threads:[~2019-08-12 20:10 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-08-12 20:09 [PATCH v6 1/2] fork: extend clone3() to support setting a PID Adrian Reber
2019-08-12 20:09 ` Adrian Reber [this message]
2019-08-13 14:46 ` [PATCH v6 2/2] selftests: add tests for clone3() Christian Brauner
2019-08-13 16:14 ` Christian Brauner
2019-08-12 20:43 ` [PATCH v6 1/2] fork: extend clone3() to support setting a PID Andrei Vagin
2019-08-12 21:02 ` Adrian Reber
2019-08-12 21:50 ` Christian Brauner
2019-08-12 21:53 ` Andrei Vagin
2019-08-13 9:45 ` Christian Brauner
2019-08-13 14:30 ` Oleg Nesterov
2019-08-14 10:04 ` Christian Brauner
2019-08-14 9:50 ` Pavel Emelianov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190812200939.23784-2-areber@redhat.com \
--to=areber@redhat.com \
--cc=0x7f454c46@gmail.com \
--cc=avagin@gmail.com \
--cc=christian.brauner@ubuntu.com \
--cc=ebiederm@xmission.com \
--cc=jannh@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=oleg@redhat.com \
--cc=rppt@linux.ibm.com \
--cc=rstoyanov1@gmail.com \
--cc=xemul@virtuozzo.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.