forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
5.3 KiB
199 lines
5.3 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
|
|
/* Based on Christian Brauner's clone3() example */ |
|
|
|
#define _GNU_SOURCE |
|
#include <errno.h> |
|
#include <inttypes.h> |
|
#include <linux/types.h> |
|
#include <linux/sched.h> |
|
#include <stdint.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" |
|
#include "clone3_selftests.h" |
|
|
|
enum test_mode { |
|
CLONE3_ARGS_NO_TEST, |
|
CLONE3_ARGS_ALL_0, |
|
CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG, |
|
CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG, |
|
CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG, |
|
CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG, |
|
}; |
|
|
|
static int call_clone3(uint64_t flags, size_t size, enum test_mode test_mode) |
|
{ |
|
struct __clone_args args = { |
|
.flags = flags, |
|
.exit_signal = SIGCHLD, |
|
}; |
|
|
|
struct clone_args_extended { |
|
struct __clone_args args; |
|
__aligned_u64 excess_space[2]; |
|
} args_ext; |
|
|
|
pid_t pid = -1; |
|
int status; |
|
|
|
memset(&args_ext, 0, sizeof(args_ext)); |
|
if (size > sizeof(struct __clone_args)) |
|
args_ext.excess_space[1] = 1; |
|
|
|
if (size == 0) |
|
size = sizeof(struct __clone_args); |
|
|
|
switch (test_mode) { |
|
case CLONE3_ARGS_NO_TEST: |
|
/* |
|
* Uses default 'flags' and 'SIGCHLD' |
|
* assignment. |
|
*/ |
|
break; |
|
case CLONE3_ARGS_ALL_0: |
|
args.flags = 0; |
|
args.exit_signal = 0; |
|
break; |
|
case CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG: |
|
args.exit_signal = 0xbadc0ded00000000ULL; |
|
break; |
|
case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG: |
|
args.exit_signal = 0x0000000080000000ULL; |
|
break; |
|
case CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG: |
|
args.exit_signal = 0x0000000000000100ULL; |
|
break; |
|
case CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG: |
|
args.exit_signal = 0x00000000000000f0ULL; |
|
break; |
|
} |
|
|
|
memcpy(&args_ext.args, &args, sizeof(struct __clone_args)); |
|
|
|
pid = sys_clone3((struct __clone_args *)&args_ext, 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); |
|
} |
|
|
|
ksft_print_msg("I am the parent (%d). My child's pid is %d\n", |
|
getpid(), pid); |
|
|
|
if (waitpid(-1, &status, __WALL) < 0) { |
|
ksft_print_msg("Child returned %s\n", strerror(errno)); |
|
return -errno; |
|
} |
|
if (WEXITSTATUS(status)) |
|
return WEXITSTATUS(status); |
|
|
|
return 0; |
|
} |
|
|
|
static void test_clone3(uint64_t flags, size_t size, int expected, |
|
enum test_mode test_mode) |
|
{ |
|
int ret; |
|
|
|
ksft_print_msg( |
|
"[%d] Trying clone3() with flags %#" PRIx64 " (size %zu)\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_test_result_fail( |
|
"[%d] Result (%d) is different than expected (%d)\n", |
|
getpid(), ret, expected); |
|
else |
|
ksft_test_result_pass( |
|
"[%d] Result (%d) matches expectation (%d)\n", |
|
getpid(), ret, expected); |
|
} |
|
|
|
int main(int argc, char *argv[]) |
|
{ |
|
uid_t uid = getuid(); |
|
|
|
ksft_print_header(); |
|
ksft_set_plan(17); |
|
test_clone3_supported(); |
|
|
|
/* Just a simple clone3() should return 0.*/ |
|
test_clone3(0, 0, 0, CLONE3_ARGS_NO_TEST); |
|
|
|
/* Do a clone3() in a new PID NS.*/ |
|
if (uid == 0) |
|
test_clone3(CLONE_NEWPID, 0, 0, CLONE3_ARGS_NO_TEST); |
|
else |
|
ksft_test_result_skip("Skipping clone3() with CLONE_NEWPID\n"); |
|
|
|
/* Do a clone3() with CLONE_ARGS_SIZE_VER0. */ |
|
test_clone3(0, CLONE_ARGS_SIZE_VER0, 0, CLONE3_ARGS_NO_TEST); |
|
|
|
/* Do a clone3() with CLONE_ARGS_SIZE_VER0 - 8 */ |
|
test_clone3(0, CLONE_ARGS_SIZE_VER0 - 8, -EINVAL, CLONE3_ARGS_NO_TEST); |
|
|
|
/* Do a clone3() with sizeof(struct clone_args) + 8 */ |
|
test_clone3(0, sizeof(struct __clone_args) + 8, 0, CLONE3_ARGS_NO_TEST); |
|
|
|
/* Do a clone3() with exit_signal having highest 32 bits non-zero */ |
|
test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_BIG); |
|
|
|
/* Do a clone3() with negative 32-bit exit_signal */ |
|
test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_NEG); |
|
|
|
/* Do a clone3() with exit_signal not fitting into CSIGNAL mask */ |
|
test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_CSIG); |
|
|
|
/* Do a clone3() with NSIG < exit_signal < CSIG */ |
|
test_clone3(0, 0, -EINVAL, CLONE3_ARGS_INVAL_EXIT_SIGNAL_NSIG); |
|
|
|
test_clone3(0, sizeof(struct __clone_args) + 8, 0, CLONE3_ARGS_ALL_0); |
|
|
|
test_clone3(0, sizeof(struct __clone_args) + 16, -E2BIG, |
|
CLONE3_ARGS_ALL_0); |
|
|
|
test_clone3(0, sizeof(struct __clone_args) * 2, -E2BIG, |
|
CLONE3_ARGS_ALL_0); |
|
|
|
/* Do a clone3() with > page size */ |
|
test_clone3(0, getpagesize() + 8, -E2BIG, CLONE3_ARGS_NO_TEST); |
|
|
|
/* Do a clone3() with CLONE_ARGS_SIZE_VER0 in a new PID NS. */ |
|
if (uid == 0) |
|
test_clone3(CLONE_NEWPID, CLONE_ARGS_SIZE_VER0, 0, |
|
CLONE3_ARGS_NO_TEST); |
|
else |
|
ksft_test_result_skip("Skipping clone3() with CLONE_NEWPID\n"); |
|
|
|
/* Do a clone3() with CLONE_ARGS_SIZE_VER0 - 8 in a new PID NS */ |
|
test_clone3(CLONE_NEWPID, CLONE_ARGS_SIZE_VER0 - 8, -EINVAL, |
|
CLONE3_ARGS_NO_TEST); |
|
|
|
/* Do a clone3() with sizeof(struct clone_args) + 8 in a new PID NS */ |
|
if (uid == 0) |
|
test_clone3(CLONE_NEWPID, sizeof(struct __clone_args) + 8, 0, |
|
CLONE3_ARGS_NO_TEST); |
|
else |
|
ksft_test_result_skip("Skipping clone3() with CLONE_NEWPID\n"); |
|
|
|
/* Do a clone3() with > page size in a new PID NS */ |
|
test_clone3(CLONE_NEWPID, getpagesize() + 8, -E2BIG, |
|
CLONE3_ARGS_NO_TEST); |
|
|
|
return !ksft_get_fail_cnt() ? ksft_exit_pass() : ksft_exit_fail(); |
|
}
|
|
|