blob: a0f1989f8b1be60ff64ac251d06eb47419e753c4 [file] [log] [blame] [edit]
// 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();
}