linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
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


  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).