Path: blob/master/tools/testing/selftests/clone3/clone3.c
26285 views
// SPDX-License-Identifier: GPL-2.012/* Based on Christian Brauner's clone3() example */34#define _GNU_SOURCE5#include <errno.h>6#include <inttypes.h>7#include <linux/types.h>8#include <linux/sched.h>9#include <stdbool.h>10#include <stdint.h>11#include <stdio.h>12#include <stdlib.h>13#include <sys/syscall.h>14#include <sys/types.h>15#include <sys/un.h>16#include <sys/wait.h>17#include <unistd.h>18#include <sched.h>1920#include "../kselftest.h"21#include "clone3_selftests.h"2223enum test_mode {24CLONE3_ARGS_NO_TEST,25CLONE3_ARGS_ALL_0,26CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG,27CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG,28CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG,29CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG,30};3132static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode)33{34struct __clone_args args = {35.flags = flags,36.exit_signal = SIGCHLD,37};3839struct clone_args_extended {40struct __clone_args args;41__aligned_u64 excess_space[2];42} args_ext;4344pid_t pid = -1;45int status;4647memset(&args_ext, 0, sizeof(args_ext));48if (size > sizeof(struct __clone_args))49args_ext.excess_space[1] = 1;5051if (size == 0)52size = sizeof(struct __clone_args);5354switch (test_mode) {55case CLONE3_ARGS_NO_TEST:56/*57* Uses default 'flags' and 'SIGCHLD'58* assignment.59*/60break;61case CLONE3_ARGS_ALL_0:62args.flags = 0;63args.exit_signal = 0;64break;65case CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG:66args.exit_signal = 0xbadc0ded00000000ULL;67break;68case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG:69args.exit_signal = 0x0000000080000000ULL;70break;71case CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG:72args.exit_signal = 0x0000000000000100ULL;73break;74case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG:75args.exit_signal = 0x00000000000000f0ULL;76break;77}7879memcpy(&args_ext.args, &args, sizeof(struct __clone_args));8081pid = sys_clone3((struct __clone_args *)&args_ext, size);82if (pid < 0) {83ksft_print_msg("%s - Failed to create new process\n",84strerror(errno));85return -errno;86}8788if (pid == 0) {89ksft_print_msg("I am the child, my PID is %d\n", getpid());90_exit(EXIT_SUCCESS);91}9293ksft_print_msg("I am the parent (%d). My child's pid is %d\n",94getpid(), pid);9596if (waitpid(-1, &status, __WALL) < 0) {97ksft_print_msg("waitpid() returned %s\n", strerror(errno));98return -errno;99}100if (!WIFEXITED(status)) {101ksft_print_msg("Child did not exit normally, status 0x%x\n",102status);103return EXIT_FAILURE;104}105if (WEXITSTATUS(status))106return WEXITSTATUS(status);107108return 0;109}110111static bool test_clone3(uint64_t flags, size_t size, int expected,112enum test_mode test_mode)113{114int ret;115116ksft_print_msg(117"[%d] Trying clone3() with flags %#" PRIx64 " (size %zu)\n",118getpid(), flags, size);119ret = call_clone3(flags, size, test_mode);120ksft_print_msg("[%d] clone3() with flags says: %d expected %d\n",121getpid(), ret, expected);122if (ret != expected) {123ksft_print_msg(124"[%d] Result (%d) is different than expected (%d)\n",125getpid(), ret, expected);126return false;127}128129return true;130}131132typedef bool (*filter_function)(void);133typedef size_t (*size_function)(void);134135static bool not_root(void)136{137if (getuid() != 0) {138ksft_print_msg("Not running as root\n");139return true;140}141142return false;143}144145static bool no_timenamespace(void)146{147if (not_root())148return true;149150if (!access("/proc/self/ns/time", F_OK))151return false;152153ksft_print_msg("Time namespaces are not supported\n");154return true;155}156157static size_t page_size_plus_8(void)158{159return getpagesize() + 8;160}161162struct test {163const char *name;164uint64_t flags;165size_t size;166size_function size_function;167int expected;168enum test_mode test_mode;169filter_function filter;170};171172static const struct test tests[] = {173{174.name = "simple clone3()",175.flags = 0,176.size = 0,177.expected = 0,178.test_mode = CLONE3_ARGS_NO_TEST,179},180{181.name = "clone3() in a new PID_NS",182.flags = CLONE_NEWPID,183.size = 0,184.expected = 0,185.test_mode = CLONE3_ARGS_NO_TEST,186.filter = not_root,187},188{189.name = "CLONE_ARGS_SIZE_VER0",190.flags = 0,191.size = CLONE_ARGS_SIZE_VER0,192.expected = 0,193.test_mode = CLONE3_ARGS_NO_TEST,194},195{196.name = "CLONE_ARGS_SIZE_VER0 - 8",197.flags = 0,198.size = CLONE_ARGS_SIZE_VER0 - 8,199.expected = -EINVAL,200.test_mode = CLONE3_ARGS_NO_TEST,201},202{203.name = "sizeof(struct clone_args) + 8",204.flags = 0,205.size = sizeof(struct __clone_args) + 8,206.expected = 0,207.test_mode = CLONE3_ARGS_NO_TEST,208},209{210.name = "exit_signal with highest 32 bits non-zero",211.flags = 0,212.size = 0,213.expected = -EINVAL,214.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG,215},216{217.name = "negative 32-bit exit_signal",218.flags = 0,219.size = 0,220.expected = -EINVAL,221.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG,222},223{224.name = "exit_signal not fitting into CSIGNAL mask",225.flags = 0,226.size = 0,227.expected = -EINVAL,228.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG,229},230{231.name = "NSIG < exit_signal < CSIG",232.flags = 0,233.size = 0,234.expected = -EINVAL,235.test_mode = CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG,236},237{238.name = "Arguments sizeof(struct clone_args) + 8",239.flags = 0,240.size = sizeof(struct __clone_args) + 8,241.expected = 0,242.test_mode = CLONE3_ARGS_ALL_0,243},244{245.name = "Arguments sizeof(struct clone_args) + 16",246.flags = 0,247.size = sizeof(struct __clone_args) + 16,248.expected = -E2BIG,249.test_mode = CLONE3_ARGS_ALL_0,250},251{252.name = "Arguments sizeof(struct clone_arg) * 2",253.flags = 0,254.size = sizeof(struct __clone_args) + 16,255.expected = -E2BIG,256.test_mode = CLONE3_ARGS_ALL_0,257},258{259.name = "Arguments > page size",260.flags = 0,261.size_function = page_size_plus_8,262.expected = -E2BIG,263.test_mode = CLONE3_ARGS_NO_TEST,264},265{266.name = "CLONE_ARGS_SIZE_VER0 in a new PID NS",267.flags = CLONE_NEWPID,268.size = CLONE_ARGS_SIZE_VER0,269.expected = 0,270.test_mode = CLONE3_ARGS_NO_TEST,271.filter = not_root,272},273{274.name = "CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS",275.flags = CLONE_NEWPID,276.size = CLONE_ARGS_SIZE_VER0 - 8,277.expected = -EINVAL,278.test_mode = CLONE3_ARGS_NO_TEST,279},280{281.name = "sizeof(struct clone_args) + 8 in a new PID NS",282.flags = CLONE_NEWPID,283.size = sizeof(struct __clone_args) + 8,284.expected = 0,285.test_mode = CLONE3_ARGS_NO_TEST,286.filter = not_root,287},288{289.name = "Arguments > page size in a new PID NS",290.flags = CLONE_NEWPID,291.size_function = page_size_plus_8,292.expected = -E2BIG,293.test_mode = CLONE3_ARGS_NO_TEST,294},295{296.name = "New time NS",297.flags = CLONE_NEWTIME,298.size = 0,299.expected = 0,300.test_mode = CLONE3_ARGS_NO_TEST,301.filter = no_timenamespace,302},303{304.name = "exit signal (SIGCHLD) in flags",305.flags = SIGCHLD,306.size = 0,307.expected = -EINVAL,308.test_mode = CLONE3_ARGS_NO_TEST,309},310};311312int main(int argc, char *argv[])313{314size_t size;315int i;316317ksft_print_header();318ksft_set_plan(ARRAY_SIZE(tests));319test_clone3_supported();320321for (i = 0; i < ARRAY_SIZE(tests); i++) {322if (tests[i].filter && tests[i].filter()) {323ksft_test_result_skip("%s\n", tests[i].name);324continue;325}326327if (tests[i].size_function)328size = tests[i].size_function();329else330size = tests[i].size;331332ksft_print_msg("Running test '%s'\n", tests[i].name);333334ksft_test_result(test_clone3(tests[i].flags, size,335tests[i].expected,336tests[i].test_mode),337"%s\n", tests[i].name);338}339340ksft_finished();341}342343344