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