linux/tools/testing/selftests/clone3/clone3.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2
   3/* Based on Christian Brauner's clone3() example */
   4
   5#define _GNU_SOURCE
   6#include <errno.h>
   7#include <inttypes.h>
   8#include <linux/types.h>
   9#include <linux/sched.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>
  19
  20#include "../kselftest.h"
  21#include "clone3_selftests.h"
  22
  23enum test_mode {
  24        CLONE3_ARGS_NO_TEST,
  25        CLONE3_ARGS_ALL_0,
  26        CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG,
  27        CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG,
  28        CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG,
  29        CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG,
  30};
  31
  32static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode)
  33{
  34        struct __clone_args args = {
  35                .flags = flags,
  36                .exit_signal = SIGCHLD,
  37        };
  38
  39        struct clone_args_extended {
  40                struct __clone_args args;
  41                __aligned_u64 excess_space[2];
  42        } args_ext;
  43
  44        pid_t pid = -1;
  45        int status;
  46
  47        memset(&args_ext, 0, sizeof(args_ext));
  48        if (size > sizeof(struct __clone_args))
  49                args_ext.excess_space[1] = 1;
  50
  51        if (size == 0)
  52                size = sizeof(struct __clone_args);
  53
  54        switch (test_mode) {
  55        case CLONE3_ARGS_ALL_0:
  56                args.flags = 0;
  57                args.exit_signal = 0;
  58                break;
  59        case CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG:
  60                args.exit_signal = 0xbadc0ded00000000ULL;
  61                break;
  62        case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG:
  63                args.exit_signal = 0x0000000080000000ULL;
  64                break;
  65        case CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG:
  66                args.exit_signal = 0x0000000000000100ULL;
  67                break;
  68        case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG:
  69                args.exit_signal = 0x00000000000000f0ULL;
  70                break;
  71        }
  72
  73        memcpy(&args_ext.args, &args, sizeof(struct __clone_args));
  74
  75        pid = sys_clone3((struct __clone_args *)&args_ext, size);
  76        if (pid < 0) {
  77                ksft_print_msg("%s - Failed to create new process\n",
  78                                strerror(errno));
  79                return -errno;
  80        }
  81
  82        if (pid == 0) {
  83                ksft_print_msg("I am the child, my PID is %d\n", getpid());
  84                _exit(EXIT_SUCCESS);
  85        }
  86
  87        ksft_print_msg("I am the parent (%d). My child's pid is %d\n",
  88                        getpid(), pid);
  89
  90        if (waitpid(-1, &status, __WALL) < 0) {
  91                ksft_print_msg("Child returned %s\n", strerror(errno));
  92                return -errno;
  93        }
  94        if (WEXITSTATUS(status))
  95                return WEXITSTATUS(status);
  96
  97        return 0;
  98}
  99
 100static void test_clone3(uint64_t flags, size_t size, int expected,
 101                       enum test_mode test_mode)
 102{
 103        int ret;
 104
 105        ksft_print_msg(
 106                "[%d] Trying clone3() with flags %#" PRIx64 " (size %zu)\n",
 107                getpid(), flags, size);
 108        ret = call_clone3(flags, size, test_mode);
 109        ksft_print_msg("[%d] clone3() with flags says: %d expected %d\n",
 110                        getpid(), ret, expected);
 111        if (ret != expected)
 112                ksft_test_result_fail(
 113                        "[%d] Result (%d) is different than expected (%d)\n",
 114                        getpid(), ret, expected);
 115        else
 116                ksft_test_result_pass(
 117                        "[%d] Result (%d) matches expectation (%d)\n",
 118                        getpid(), ret, expected);
 119}
 120
 121int main(int argc, char *argv[])
 122{
 123        pid_t pid;
 124
 125        uid_t uid = getuid();
 126
 127        ksft_print_header();
 128        ksft_set_plan(17);
 129        test_clone3_supported();
 130
 131        /* Just a simple clone3() should return 0.*/
 132        test_clone3(0, 0, 0, CLONE3_ARGS_NO_TEST);
 133
 134        /* Do a clone3() in a new PID NS.*/
 135        if (uid == 0)
 136                test_clone3(CLONE_NEWPID, 0, 0, CLONE3_ARGS_NO_TEST);
 137        else
 138                ksft_test_result_skip("Skipping clone3() with CLONE_NEWPID\n");
 139
 140        /* Do a clone3() with CLONE_ARGS_SIZE_VER0. */
 141        test_clone3(0, CLONE_ARGS_SIZE_VER0, 0, CLONE3_ARGS_NO_TEST);
 142
 143        /* Do a clone3() with CLONE_ARGS_SIZE_VER0 - 8 */
 144        test_clone3(0, CLONE_ARGS_SIZE_VER0 - 8, -EINVAL, CLONE3_ARGS_NO_TEST);
 145
 146        /* Do a clone3() with sizeof(struct clone_args) + 8 */
 147        test_clone3(0, sizeof(struct __clone_args) + 8, 0, CLONE3_ARGS_NO_TEST);
 148
 149        /* Do a clone3() with exit_signal having highest 32 bits non-zero */
 150        test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG);
 151
 152        /* Do a clone3() with negative 32-bit exit_signal */
 153        test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG);
 154
 155        /* Do a clone3() with exit_signal not fitting into CSIGNAL mask */
 156        test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG);
 157
 158        /* Do a clone3() with NSIG < exit_signal < CSIG */
 159        test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG);
 160
 161        test_clone3(0, sizeof(struct __clone_args) + 8, 0, CLONE3_ARGS_ALL_0);
 162
 163        test_clone3(0, sizeof(struct __clone_args) + 16, -E2BIG,
 164                        CLONE3_ARGS_ALL_0);
 165
 166        test_clone3(0, sizeof(struct __clone_args) * 2, -E2BIG,
 167                        CLONE3_ARGS_ALL_0);
 168
 169        /* Do a clone3() with > page size */
 170        test_clone3(0, getpagesize() + 8, -E2BIG, CLONE3_ARGS_NO_TEST);
 171
 172        /* Do a clone3() with CLONE_ARGS_SIZE_VER0 in a new PID NS. */
 173        if (uid == 0)
 174                test_clone3(CLONE_NEWPID, CLONE_ARGS_SIZE_VER0, 0,
 175                                CLONE3_ARGS_NO_TEST);
 176        else
 177                ksft_test_result_skip("Skipping clone3() with CLONE_NEWPID\n");
 178
 179        /* Do a clone3() with CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS */
 180        test_clone3(CLONE_NEWPID, CLONE_ARGS_SIZE_VER0 - 8, -EINVAL,
 181                        CLONE3_ARGS_NO_TEST);
 182
 183        /* Do a clone3() with sizeof(struct clone_args) + 8 in a new PID NS */
 184        if (uid == 0)
 185                test_clone3(CLONE_NEWPID, sizeof(struct __clone_args) + 8, 0,
 186                                CLONE3_ARGS_NO_TEST);
 187        else
 188                ksft_test_result_skip("Skipping clone3() with CLONE_NEWPID\n");
 189
 190        /* Do a clone3() with > page size in a new PID NS */
 191        test_clone3(CLONE_NEWPID, getpagesize() + 8, -E2BIG,
 192                        CLONE3_ARGS_NO_TEST);
 193
 194        return !ksft_get_fail_cnt() ? ksft_exit_pass() : ksft_exit_fail();
 195}
 196