linux-stable/tools/testing/selftests/clone3/clone3.c
Tiezhu Yang 4c004ed952 selftests/clone3: Fix broken test under !CONFIG_TIME_NS
commit fc7f04dc23 upstream.

When execute the following command to test clone3 under !CONFIG_TIME_NS:

  # make headers && cd tools/testing/selftests/clone3 && make && ./clone3

we can see the following error info:

  # [7538] Trying clone3() with flags 0x80 (size 0)
  # Invalid argument - Failed to create new process
  # [7538] clone3() with flags says: -22 expected 0
  not ok 18 [7538] Result (-22) is different than expected (0)
  ...
  # Totals: pass:18 fail:1 xfail:0 xpass:0 skip:0 error:0

This is because if CONFIG_TIME_NS is not set, but the flag
CLONE_NEWTIME (0x80) is used to clone a time namespace, it
will return -EINVAL in copy_time_ns().

If kernel does not support CONFIG_TIME_NS, /proc/self/ns/time
will be not exist, and then we should skip clone3() test with
CLONE_NEWTIME.

With this patch under !CONFIG_TIME_NS:

  # make headers && cd tools/testing/selftests/clone3 && make && ./clone3
  ...
  # Time namespaces are not supported
  ok 18 # SKIP Skipping clone3() with CLONE_NEWTIME
  ...
  # Totals: pass:18 fail:0 xfail:0 xpass:0 skip:1 error:0

Link: https://lkml.kernel.org/r/1689066814-13295-1-git-send-email-yangtiezhu@loongson.cn
Fixes: 515bddf0ec ("selftests/clone3: test clone3 with CLONE_NEWTIME")
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Shuah Khan <shuah@kernel.org>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2023-11-28 17:20:05 +00:00

210 lines
5.6 KiB
C

// 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(19);
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);
/* Do a clone3() in a new time namespace */
if (access("/proc/self/ns/time", F_OK) == 0) {
test_clone3(CLONE_NEWTIME, 0, 0, CLONE3_ARGS_NO_TEST);
} else {
ksft_print_msg("Time namespaces are not supported\n");
ksft_test_result_skip("Skipping clone3() with CLONE_NEWTIME\n");
}
/* Do a clone3() with exit signal (SIGCHLD) in flags */
test_clone3(SIGCHLD, 0, -EINVAL, CLONE3_ARGS_NO_TEST);
ksft_finished();
}