// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) 2020 Viresh Kumar */ /*\ * [Description] * * Basic clone3() test. */ #define _GNU_SOURCE #include #include #include "tst_test.h" #include "lapi/clone.h" #include "lapi/pidfd_send_signal.h" #define CHILD_SIGNAL SIGUSR1 #define DATA 777 static int pidfd, child_tid, parent_tid, parent_received_signal; static volatile int child_received_signal, child_data; static struct clone_args *args; static struct tcase { uint64_t flags; int exit_signal; } tcases[] = { {0, SIGCHLD}, {0, SIGUSR2}, {CLONE_FS, SIGCHLD}, {CLONE_NEWPID, SIGCHLD}, {CLONE_PARENT_SETTID | CLONE_CHILD_SETTID | CLONE_PIDFD, SIGCHLD}, }; static void parent_rx_signal(int sig) { parent_received_signal = sig; } static void child_rx_signal(int sig, siginfo_t *info, void *ucontext) { (void) ucontext; child_received_signal = sig; child_data = info->si_value.sival_int; } static struct sigaction psig_action = { .sa_handler = parent_rx_signal, }; static struct sigaction csig_action = { .sa_sigaction = child_rx_signal, .sa_flags = SA_SIGINFO, }; static siginfo_t uinfo = { .si_signo = CHILD_SIGNAL, .si_code = SI_QUEUE, .si_value.sival_int = DATA, }; static void do_child(int clone_pidfd) { int count = 1000; if (clone_pidfd) { child_received_signal = 0; child_data = 0; SAFE_SIGACTION(CHILD_SIGNAL, &csig_action, NULL); TST_CHECKPOINT_WAKE(0); while (child_received_signal != CHILD_SIGNAL && --count) usleep(100); if (!child_received_signal) { tst_res(TFAIL, "Child haven't got signal"); exit(0); } if (child_received_signal != CHILD_SIGNAL) { tst_res(TFAIL, "Child got %s (%i) signal expected %s", tst_strsig(child_received_signal), child_received_signal, tst_strsig(CHILD_SIGNAL)); exit(0); } tst_res(TPASS, "Child got correct signal %s", tst_strsig(CHILD_SIGNAL)); if (child_data != DATA) { tst_res(TFAIL, "Child got wrong si_value=%i expected %i", child_data, DATA); } else { tst_res(TPASS, "Child got correct si_value"); } } exit(0); } static void run(unsigned int n) { struct tcase *tc = &tcases[n]; int status, clone_pidfd = tc->flags & CLONE_PIDFD; pid_t pid; args->flags = tc->flags; args->pidfd = (uint64_t)(&pidfd); args->child_tid = (uint64_t)(&child_tid); args->parent_tid = (uint64_t)(&parent_tid); args->exit_signal = tc->exit_signal; args->stack = 0; args->stack_size = 0; args->tls = 0; parent_received_signal = 0; SAFE_SIGACTION(tc->exit_signal, &psig_action, NULL); TEST(pid = clone3(args, sizeof(*args))); if (pid < 0) { tst_res(TFAIL | TTERRNO, "clone3() failed (%d)", n); return; } if (!pid) do_child(clone_pidfd); /* Need to send signal to child process */ if (clone_pidfd) { TST_CHECKPOINT_WAIT(0); TEST(pidfd_send_signal(pidfd, CHILD_SIGNAL, &uinfo, 0)); if (TST_RET != 0) { tst_res(TFAIL | TTERRNO, "pidfd_send_signal() failed"); return; } } SAFE_WAITPID(pid, &status, __WALL); if (!parent_received_signal) { tst_res(TFAIL, "Parent haven't got signal"); return; } if (parent_received_signal != tc->exit_signal) { tst_res(TFAIL, "Parent got %s (%i) signal expected %s", tst_strsig(parent_received_signal), parent_received_signal, tst_strsig(tc->exit_signal)); return; } tst_res(TPASS, "Parent got correct signal %s", tst_strsig(parent_received_signal)); } static void setup(void) { clone3_supported_by_kernel(); uinfo.si_pid = getpid(); uinfo.si_uid = getuid(); } static struct tst_test test = { .tcnt = ARRAY_SIZE(tcases), .test = run, .setup = setup, .needs_root = 1, .needs_checkpoints = 1, .bufs = (struct tst_buffers []) { {&args, .size = sizeof(*args)}, {}, } };