diff options
-rw-r--r-- | sysdeps/unix/sysv/linux/Makefile | 9 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/tst-align-clone-internal.c | 68 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/tst-clone2-internal.c | 126 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/tst-clone3-internal.c | 99 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/tst-getpid1-internal.c | 133 | ||||
-rw-r--r-- | sysdeps/unix/sysv/linux/tst-misalign-clone-internal.c | 74 |
6 files changed, 509 insertions, 0 deletions
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index ed0c0d27f4..cceb16be05 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -139,6 +139,15 @@ tests-time64 += \ tst-sigtimedwait-time64 \ tst-timerfd-time64 \ +tests-clone-internal = \ + tst-align-clone-internal \ + tst-clone2-internal \ + tst-clone3-internal \ + tst-getpid1-internal \ + tst-misalign-clone-internal +tests-internal += $(tests-clone-internal) +tests-static += $(tests-clone-internal) + CFLAGS-tst-sigcontext-get_pc.c = -fasynchronous-unwind-tables # Generate the list of SYS_* macros for the system calls (__NR_* diff --git a/sysdeps/unix/sysv/linux/tst-align-clone-internal.c b/sysdeps/unix/sysv/linux/tst-align-clone-internal.c new file mode 100644 index 0000000000..f3f5b46378 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-align-clone-internal.c @@ -0,0 +1,68 @@ +/* Verify that the clone child stack is properly aligned. + Copyright (C) 2021 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <sched.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> +#include <tst-stack-align.h> +#include <clone_internal.h> +#include <support/xunistd.h> +#include <support/check.h> + +static int +f (void *arg) +{ + puts ("in f"); + + return TEST_STACK_ALIGN () ? 1 : 0; +} + +static int +do_test (void) +{ + puts ("in main"); + + if (TEST_STACK_ALIGN ()) + FAIL_EXIT1 ("stack alignment failed"); + +#ifdef __ia64__ +# define STACK_SIZE 256 * 1024 +#else +# define STACK_SIZE 128 * 1024 +#endif + char st[STACK_SIZE] __attribute__ ((aligned)); + struct clone_args clone_args = + { + .stack = (uintptr_t) st, + .stack_size = sizeof (st), + }; + pid_t p = __clone_internal (&clone_args, f, 0); + TEST_VERIFY (p != -1); + + int e; + xwaitpid (p, &e, __WCLONE); + TEST_VERIFY (WIFEXITED (e)); + TEST_COMPARE (WEXITSTATUS (e), 0); + return 0; +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/tst-clone2-internal.c b/sysdeps/unix/sysv/linux/tst-clone2-internal.c new file mode 100644 index 0000000000..fd3a55158c --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-clone2-internal.c @@ -0,0 +1,126 @@ +/* Test if CLONE_VM does not change pthread pid/tid field (BZ #19957) + Copyright (C) 2021 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <sched.h> +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <stddef.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/syscall.h> +#include <clone_internal.h> +#include <support/xunistd.h> +#include <support/check.h> + +static int sig; +static int pipefd[2]; + +static int +f (void *a) +{ + close (pipefd[0]); + + pid_t ppid = getppid (); + pid_t pid = getpid (); + pid_t tid = gettid (); + + if (write (pipefd[1], &ppid, sizeof ppid) != sizeof (ppid)) + FAIL_EXIT1 ("write ppid failed\n"); + if (write (pipefd[1], &pid, sizeof pid) != sizeof (pid)) + FAIL_EXIT1 ("write pid failed\n"); + if (write (pipefd[1], &tid, sizeof tid) != sizeof (tid)) + FAIL_EXIT1 ("write tid failed\n"); + + return 0; +} + + +static int +do_test (void) +{ + sig = SIGRTMIN; + sigset_t ss; + sigemptyset (&ss); + sigaddset (&ss, sig); + if (sigprocmask (SIG_BLOCK, &ss, NULL) != 0) + FAIL_EXIT1 ("sigprocmask failed: %m"); + + if (pipe2 (pipefd, O_CLOEXEC)) + FAIL_EXIT1 ("pipe failed: %m"); + +#ifdef __ia64__ +# define STACK_SIZE 256 * 1024 +#else +# define STACK_SIZE 128 * 1024 +#endif + char st[STACK_SIZE] __attribute__ ((aligned)); + struct clone_args clone_args = + { + .stack = (uintptr_t) st, + .stack_size = sizeof (st), + }; + pid_t p = __clone_internal (&clone_args, f, 0); + + close (pipefd[1]); + + if (p == -1) + FAIL_EXIT1("clone failed: %m"); + + pid_t ppid, pid, tid; + if (read (pipefd[0], &ppid, sizeof pid) != sizeof pid) + { + kill (p, SIGKILL); + FAIL_EXIT1 ("read ppid failed: %m"); + } + if (read (pipefd[0], &pid, sizeof pid) != sizeof pid) + { + kill (p, SIGKILL); + FAIL_EXIT1 ("read pid failed: %m"); + } + if (read (pipefd[0], &tid, sizeof tid) != sizeof tid) + { + kill (p, SIGKILL); + FAIL_EXIT1 ("read tid failed: %m"); + } + + close (pipefd[0]); + + pid_t own_pid = getpid (); + pid_t own_tid = syscall (__NR_gettid); + + /* Some sanity checks for clone syscall: returned ppid should be current + pid and both returned tid/pid should be different from current one. */ + if ((ppid != own_pid) || (pid == own_pid) || (tid == own_tid)) + FAIL_RET ("ppid=%i pid=%i tid=%i | own_pid=%i own_tid=%i", + (int)ppid, (int)pid, (int)tid, (int)own_pid, (int)own_tid); + + int e; + xwaitpid (p, &e, __WCLONE); + TEST_VERIFY (WIFEXITED (e)); + TEST_COMPARE (WEXITSTATUS (e), 0); + return 0; +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/tst-clone3-internal.c b/sysdeps/unix/sysv/linux/tst-clone3-internal.c new file mode 100644 index 0000000000..2bdbc571e6 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-clone3-internal.c @@ -0,0 +1,99 @@ +/* Check if clone (CLONE_THREAD) does not call exit_group (BZ #21512) + Copyright (C) 2021 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <string.h> +#include <sched.h> +#include <signal.h> +#include <unistd.h> +#include <errno.h> +#include <sys/syscall.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <linux/futex.h> +#include <support/check.h> +#include <stdatomic.h> +#include <clone_internal.h> + +/* Test if clone call with CLONE_THREAD does not call exit_group. The 'f' + function returns '1', which will be used by clone thread to call the + 'exit' syscall directly. If _exit is used instead, exit_group will be + used and thus the thread group will finish with return value of '1' + (where '2' from main thread is expected.). */ + +static int +f (void *a) +{ + return 1; +} + +/* Futex wait for TID argument, similar to pthread_join internal + implementation. */ +#define wait_tid(ctid_ptr, ctid_val) \ + do { \ + __typeof (*(ctid_ptr)) __tid; \ + /* We need acquire MO here so that we synchronize with the \ + kernel's store to 0 when the clone terminates. */ \ + while ((__tid = atomic_load_explicit (ctid_ptr, \ + memory_order_acquire)) != 0) \ + futex_wait (ctid_ptr, ctid_val); \ + } while (0) + +static inline int +futex_wait (int *futexp, int val) +{ +#ifdef __NR_futex + return syscall (__NR_futex, futexp, FUTEX_WAIT, val); +#else + return syscall (__NR_futex_time64, futexp, FUTEX_WAIT, val); +#endif +} + +static int +do_test (void) +{ + char st[1024] __attribute__ ((aligned)); + int clone_flags = CLONE_THREAD; + /* Minimum required flags to used along with CLONE_THREAD. */ + clone_flags |= CLONE_VM | CLONE_SIGHAND; + /* We will used ctid to call on futex to wait for thread exit. */ + clone_flags |= CLONE_CHILD_CLEARTID; + /* Initialize with a known value. ctid is set to zero by the kernel after the + cloned thread has exited. */ +#define CTID_INIT_VAL 1 + pid_t ctid = CTID_INIT_VAL; + pid_t tid; + + struct clone_args clone_args = + { + .flags = clone_flags & ~CSIGNAL, + .exit_signal = clone_flags & CSIGNAL, + .stack = (uintptr_t) st, + .stack_size = sizeof (st), + .child_tid = (uintptr_t) &ctid, + }; + tid = __clone_internal (&clone_args, f, NULL); + if (tid == -1) + FAIL_EXIT1 ("clone failed: %m"); + + wait_tid (&ctid, CTID_INIT_VAL); + + return 2; +} + +#define EXPECTED_STATUS 2 +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/tst-getpid1-internal.c b/sysdeps/unix/sysv/linux/tst-getpid1-internal.c new file mode 100644 index 0000000000..ee69e52401 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-getpid1-internal.c @@ -0,0 +1,133 @@ +/* Verify that the parent pid is unchanged by __clone_internal. + Copyright (C) 2021 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <sched.h> +#include <signal.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <clone_internal.h> +#include <support/xunistd.h> + +#ifndef TEST_CLONE_FLAGS +#define TEST_CLONE_FLAGS 0 +#endif + +static int sig; + +static int +f (void *a) +{ + puts ("in f"); + union sigval sival; + sival.sival_int = getpid (); + printf ("pid = %d\n", sival.sival_int); + if (sigqueue (getppid (), sig, sival) != 0) + return 1; + return 0; +} + + +static int +do_test (void) +{ + int mypid = getpid (); + + sig = SIGRTMIN; + sigset_t ss; + sigemptyset (&ss); + sigaddset (&ss, sig); + if (sigprocmask (SIG_BLOCK, &ss, NULL) != 0) + { + printf ("sigprocmask failed: %m\n"); + return 1; + } + +#ifdef __ia64__ +# define STACK_SIZE 256 * 1024 +#else +# define STACK_SIZE 128 * 1024 +#endif + char st[STACK_SIZE] __attribute__ ((aligned)); + struct clone_args clone_args = + { + .flags = TEST_CLONE_FLAGS & ~CSIGNAL, + .exit_signal = TEST_CLONE_FLAGS & CSIGNAL, + .stack = (uintptr_t) st, + .stack_size = sizeof (st), + }; + pid_t p = __clone_internal (&clone_args, f, 0); + if (p == -1) + { + printf("clone failed: %m\n"); + return 1; + } + printf ("new thread: %d\n", (int) p); + + siginfo_t si; + do + if (sigwaitinfo (&ss, &si) < 0) + { + printf("sigwaitinfo failed: %m\n"); + kill (p, SIGKILL); + return 1; + } + while (si.si_signo != sig || si.si_code != SI_QUEUE); + + int e; + xwaitpid (p, &e, __WCLONE); + if (!WIFEXITED (e)) + { + if (WIFSIGNALED (e)) + printf ("died from signal %s\n", strsignal (WTERMSIG (e))); + else + puts ("did not terminate correctly"); + return 1; + } + if (WEXITSTATUS (e) != 0) + { + printf ("exit code %d\n", WEXITSTATUS (e)); + return 1; + } + + if (si.si_int != (int) p) + { + printf ("expected PID %d, got si_int %d\n", (int) p, si.si_int); + kill (p, SIGKILL); + return 1; + } + + if (si.si_pid != p) + { + printf ("expected PID %d, got si_pid %d\n", (int) p, (int) si.si_pid); + kill (p, SIGKILL); + return 1; + } + + if (getpid () != mypid) + { + puts ("my PID changed"); + return 1; + } + + return 0; +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/tst-misalign-clone-internal.c b/sysdeps/unix/sysv/linux/tst-misalign-clone-internal.c new file mode 100644 index 0000000000..5e34160238 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-misalign-clone-internal.c @@ -0,0 +1,74 @@ +/* Verify that __clone_internal properly aligns the child stack. + Copyright (C) 2021 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + <https://www.gnu.org/licenses/>. */ + +#include <sched.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> +#include <sys/wait.h> +#include <unistd.h> +#include <libc-pointer-arith.h> +#include <tst-stack-align.h> +#include <clone_internal.h> +#include <support/xunistd.h> +#include <support/check.h> + +static int +check_stack_alignment (void *arg) +{ + puts ("in f"); + + return TEST_STACK_ALIGN () ? 1 : 0; +} + +static int +do_test (void) +{ + puts ("in do_test"); + + if (TEST_STACK_ALIGN ()) + FAIL_EXIT1 ("stack isn't aligned\n"); + +#ifdef __ia64__ +# define STACK_SIZE (256 * 1024) +#else +# define STACK_SIZE (128 * 1024) +#endif + char st[STACK_SIZE + 1]; + /* NB: Align child stack to 1 byte. */ + char *stack = PTR_ALIGN_UP (&st[0], 2) + 1; + struct clone_args clone_args = + { + .stack = (uintptr_t) stack, + .stack_size = STACK_SIZE, + }; + pid_t p = __clone_internal (&clone_args, check_stack_alignment, 0); + + /* Clone must not fail. */ + TEST_VERIFY_EXIT (p != -1); + + int e; + xwaitpid (p, &e, __WCLONE); + TEST_VERIFY (WIFEXITED (e)); + TEST_COMPARE (WEXITSTATUS (e), 0); + + return 0; +} + +#include <support/test-driver.c> |