[v8,3/3] Add static tests for __clone_internal
Checks
Commit Message
---
sysdeps/unix/sysv/linux/Makefile | 9 ++
.../sysv/linux/tst-align-clone-internal.c | 87 +++++++++++
sysdeps/unix/sysv/linux/tst-clone2-internal.c | 137 ++++++++++++++++++
sysdeps/unix/sysv/linux/tst-clone3-internal.c | 99 +++++++++++++
.../unix/sysv/linux/tst-getpid1-internal.c | 133 +++++++++++++++++
.../sysv/linux/tst-misalign-clone-internal.c | 86 +++++++++++
6 files changed, 551 insertions(+)
create mode 100644 sysdeps/unix/sysv/linux/tst-align-clone-internal.c
create mode 100644 sysdeps/unix/sysv/linux/tst-clone2-internal.c
create mode 100644 sysdeps/unix/sysv/linux/tst-clone3-internal.c
create mode 100644 sysdeps/unix/sysv/linux/tst-getpid1-internal.c
create mode 100644 sysdeps/unix/sysv/linux/tst-misalign-clone-internal.c
Comments
They are quite similar to the non '-internal' tests, would be better to try
include and reimplement the difference bits that calls the __clone_internal()
intead of replicate all the tests?
On 01/06/2021 11:55, H.J. Lu wrote:
> ---
> sysdeps/unix/sysv/linux/Makefile | 9 ++
> .../sysv/linux/tst-align-clone-internal.c | 87 +++++++++++
> sysdeps/unix/sysv/linux/tst-clone2-internal.c | 137 ++++++++++++++++++
> sysdeps/unix/sysv/linux/tst-clone3-internal.c | 99 +++++++++++++
> .../unix/sysv/linux/tst-getpid1-internal.c | 133 +++++++++++++++++
> .../sysv/linux/tst-misalign-clone-internal.c | 86 +++++++++++
> 6 files changed, 551 insertions(+)
> create mode 100644 sysdeps/unix/sysv/linux/tst-align-clone-internal.c
> create mode 100644 sysdeps/unix/sysv/linux/tst-clone2-internal.c
> create mode 100644 sysdeps/unix/sysv/linux/tst-clone3-internal.c
> create mode 100644 sysdeps/unix/sysv/linux/tst-getpid1-internal.c
> create mode 100644 sysdeps/unix/sysv/linux/tst-misalign-clone-internal.c
>
> diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
> index 9469868bce..214b912921 100644
> --- a/sysdeps/unix/sysv/linux/Makefile
> +++ b/sysdeps/unix/sysv/linux/Makefile
> @@ -118,6 +118,15 @@ endif
>
> tests-internal += tst-sigcontext-get_pc
>
> +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_*
Ok.
> 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..6c3631f3db
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-align-clone-internal.c
> @@ -0,0 +1,87 @@
> +/* 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>
> +
> +static int
> +f (void *arg)
> +{
> + bool ok = true;
> +
> + puts ("in f");
> +
> + if (TEST_STACK_ALIGN ())
> + ok = false;
> +
> + return ok ? 0 : 1;
> +}
Maybe:
statit int
f (void *arg)
{
return TEST_STACK_ALIGN () ? 0 : 1;
}
> +
> +static int
> +do_test (void)
> +{
> + bool ok = true;
> +
> + puts ("in main");
> +
> + if (TEST_STACK_ALIGN ())
> + ok = false;
> +
Maybe
ok = TEST_STACK_ALIGN ();
But I think this does not really add much, so I think it would be better
to:
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);
> + if (p == -1)
> + {
> + printf("clone failed: %m\n");
> + return 1;
> + }
Use TEST_VERIFY here:
TEST_VERIFY (p != -1);
> +
> + 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)
> + ok = false;
TEST_VERIFY (WIFEXITED (status));
TEST_COMPARE (WEXITSTATUS (status), 0);
> +
> + return ok ? 0 : 1;
> +}
> +
> +#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..b8917fe713
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-clone2-internal.c
> @@ -0,0 +1,137 @@
> +/* 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]);
> +
> + int ret = 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);
> + if (!WIFEXITED (e))
> + {
> + if (WIFSIGNALED (e))
> + printf ("died from signal %s\n", strsignal (WTERMSIG (e)));
> + else
> + puts ("did not terminate correctly");
> + exit (EXIT_FAILURE);
> + }
> + if (WEXITSTATUS (e) != 0)
> + FAIL_EXIT1 ("exit code %d", WEXITSTATUS (e));
TEST_VERIFY (WIFEXITED (status));
TEST_COMPARE (WEXITSTATUS (status), 0);
> +
> + return ret;
> +}
> +
> +#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>
Ok.
> 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..6df5fd2cbc
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-misalign-clone-internal.c
> @@ -0,0 +1,86 @@
> +/* 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)
> +{
> + bool ok = true;
> +
> + puts ("in f");
> +
> + if (TEST_STACK_ALIGN ())
> + ok = false;
> +
> + return ok ? 0 : 1;
> +}
> +
> +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);
> + if (!WIFEXITED (e))
> + {
> + if (WIFSIGNALED (e))
> + printf ("died from signal %s\n", strsignal (WTERMSIG (e)));
> + FAIL_EXIT1 ("process did not terminate correctly");
> + }
> +
> + if (WEXITSTATUS (e) != 0)
> + FAIL_EXIT1 ("exit code %d", WEXITSTATUS (e));
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
>
On Tue, Jul 13, 2021 at 12:33 PM Adhemerval Zanella
<adhemerval.zanella@linaro.org> wrote:
>
> They are quite similar to the non '-internal' tests, would be better to try
> include and reimplement the difference bits that calls the __clone_internal()
> intead of replicate all the tests?
I'd like to test __clone_internal separately and leave the existing clone
tests alone.
> On 01/06/2021 11:55, H.J. Lu wrote:
> > ---
> > sysdeps/unix/sysv/linux/Makefile | 9 ++
> > .../sysv/linux/tst-align-clone-internal.c | 87 +++++++++++
> > sysdeps/unix/sysv/linux/tst-clone2-internal.c | 137 ++++++++++++++++++
> > sysdeps/unix/sysv/linux/tst-clone3-internal.c | 99 +++++++++++++
> > .../unix/sysv/linux/tst-getpid1-internal.c | 133 +++++++++++++++++
> > .../sysv/linux/tst-misalign-clone-internal.c | 86 +++++++++++
> > 6 files changed, 551 insertions(+)
> > create mode 100644 sysdeps/unix/sysv/linux/tst-align-clone-internal.c
> > create mode 100644 sysdeps/unix/sysv/linux/tst-clone2-internal.c
> > create mode 100644 sysdeps/unix/sysv/linux/tst-clone3-internal.c
> > create mode 100644 sysdeps/unix/sysv/linux/tst-getpid1-internal.c
> > create mode 100644 sysdeps/unix/sysv/linux/tst-misalign-clone-internal.c
> >
> > diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
> > index 9469868bce..214b912921 100644
> > --- a/sysdeps/unix/sysv/linux/Makefile
> > +++ b/sysdeps/unix/sysv/linux/Makefile
> > @@ -118,6 +118,15 @@ endif
> >
> > tests-internal += tst-sigcontext-get_pc
> >
> > +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_*
>
> Ok.
>
> > 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..6c3631f3db
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/tst-align-clone-internal.c
> > @@ -0,0 +1,87 @@
> > +/* 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>
> > +
> > +static int
> > +f (void *arg)
> > +{
> > + bool ok = true;
> > +
> > + puts ("in f");
> > +
> > + if (TEST_STACK_ALIGN ())
> > + ok = false;
> > +
> > + return ok ? 0 : 1;
> > +}
>
> Maybe:
>
> statit int
> f (void *arg)
> {
> return TEST_STACK_ALIGN () ? 0 : 1;
> }
Fixed.
> > +
> > +static int
> > +do_test (void)
> > +{
> > + bool ok = true;
> > +
> > + puts ("in main");
> > +
> > + if (TEST_STACK_ALIGN ())
> > + ok = false;
> > +
>
> Maybe
>
> ok = TEST_STACK_ALIGN ();
>
> But I think this does not really add much, so I think it would be better
> to:
>
> if (! TEST_STACK_ALIGN ())
> FAIL_EXIT1 ("stack alignment failed");
Fixed.
> > +#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);
> > + if (p == -1)
> > + {
> > + printf("clone failed: %m\n");
> > + return 1;
> > + }
>
> Use TEST_VERIFY here:
>
> TEST_VERIFY (p != -1);
Fixed.
> > +
> > + 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)
> > + ok = false;
>
> TEST_VERIFY (WIFEXITED (status));
> TEST_COMPARE (WEXITSTATUS (status), 0);
Fixed.
> > +
> > + return ok ? 0 : 1;
> > +}
> > +
> > +#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..b8917fe713
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/tst-clone2-internal.c
> > @@ -0,0 +1,137 @@
> > +/* 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]);
> > +
> > + int ret = 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);
> > + if (!WIFEXITED (e))
> > + {
> > + if (WIFSIGNALED (e))
> > + printf ("died from signal %s\n", strsignal (WTERMSIG (e)));
> > + else
> > + puts ("did not terminate correctly");
> > + exit (EXIT_FAILURE);
> > + }
> > + if (WEXITSTATUS (e) != 0)
> > + FAIL_EXIT1 ("exit code %d", WEXITSTATUS (e));
>
> TEST_VERIFY (WIFEXITED (status));
> TEST_COMPARE (WEXITSTATUS (status), 0);
Fixed.
> > +
> > + return ret;
> > +}
> > +
> > +#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>
>
> Ok.
>
> > 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..6df5fd2cbc
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/tst-misalign-clone-internal.c
> > @@ -0,0 +1,86 @@
> > +/* 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)
> > +{
> > + bool ok = true;
> > +
> > + puts ("in f");
> > +
> > + if (TEST_STACK_ALIGN ())
> > + ok = false;
> > +
> > + return ok ? 0 : 1;
> > +}
I made a similar change here.
> > +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);
> > + if (!WIFEXITED (e))
> > + {
> > + if (WIFSIGNALED (e))
> > + printf ("died from signal %s\n", strsignal (WTERMSIG (e)));
> > + FAIL_EXIT1 ("process did not terminate correctly");
> > + }
> > +
> > + if (WEXITSTATUS (e) != 0)
> > + FAIL_EXIT1 ("exit code %d", WEXITSTATUS (e));
Likewise.
> > + return 0;
> > +}
> > +
> > +#include <support/test-driver.c>
> >
Here is the v9 patch. OK for master?
Thanks.
On 13/07/2021 18:12, H.J. Lu wrote:
>
> Here is the v9 patch. OK for master?
>
> Thanks.
>
I think you forgot the attach the patch.
On Wed, Jul 14, 2021 at 6:18 AM Adhemerval Zanella
<adhemerval.zanella@linaro.org> wrote:
>
>
>
> On 13/07/2021 18:12, H.J. Lu wrote:
> >
> > Here is the v9 patch. OK for master?
> >
> > Thanks.
> >
>
> I think you forgot the attach the patch.
Oops. Here is the patch.
On 14/07/2021 10:32, H.J. Lu wrote:
> On Wed, Jul 14, 2021 at 6:18 AM Adhemerval Zanella
> <adhemerval.zanella@linaro.org> wrote:
>>
>>
>>
>> On 13/07/2021 18:12, H.J. Lu wrote:
>>>
>>> Here is the v9 patch. OK for master?
>>>
>>> Thanks.
>>>
>>
>> I think you forgot the attach the patch.
>
> Oops. Here is the patch.
>
LGTM, thanks.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
> From 1aa6b571479b0a2fb54e8455f45875b4f6b4b2d8 Mon Sep 17 00:00:00 2001
> From: "H.J. Lu" <hjl.tools@gmail.com>
> Date: Fri, 14 May 2021 15:23:46 -0700
> Subject: [PATCH v9] Add static tests for __clone_internal
>
> ---
> sysdeps/unix/sysv/linux/Makefile | 9 ++
> .../sysv/linux/tst-align-clone-internal.c | 68 +++++++++
> sysdeps/unix/sysv/linux/tst-clone2-internal.c | 126 +++++++++++++++++
> sysdeps/unix/sysv/linux/tst-clone3-internal.c | 99 +++++++++++++
> .../unix/sysv/linux/tst-getpid1-internal.c | 133 ++++++++++++++++++
> .../sysv/linux/tst-misalign-clone-internal.c | 74 ++++++++++
> 6 files changed, 509 insertions(+)
> create mode 100644 sysdeps/unix/sysv/linux/tst-align-clone-internal.c
> create mode 100644 sysdeps/unix/sysv/linux/tst-clone2-internal.c
> create mode 100644 sysdeps/unix/sysv/linux/tst-clone3-internal.c
> create mode 100644 sysdeps/unix/sysv/linux/tst-getpid1-internal.c
> create mode 100644 sysdeps/unix/sysv/linux/tst-misalign-clone-internal.c
>
> 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_*,
Ok.
> 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>
Ok.
> 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>
Ok.
> 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>
Ok.
> 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>
Ok.
> 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>
> --
> 2.31.1
Ok.
@@ -118,6 +118,15 @@ endif
tests-internal += tst-sigcontext-get_pc
+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_*
new file mode 100644
@@ -0,0 +1,87 @@
+/* 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>
+
+static int
+f (void *arg)
+{
+ bool ok = true;
+
+ puts ("in f");
+
+ if (TEST_STACK_ALIGN ())
+ ok = false;
+
+ return ok ? 0 : 1;
+}
+
+static int
+do_test (void)
+{
+ bool ok = true;
+
+ puts ("in main");
+
+ if (TEST_STACK_ALIGN ())
+ ok = false;
+
+#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);
+ if (p == -1)
+ {
+ printf("clone failed: %m\n");
+ return 1;
+ }
+
+ 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)
+ ok = false;
+
+ return ok ? 0 : 1;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,137 @@
+/* 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]);
+
+ int ret = 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);
+ if (!WIFEXITED (e))
+ {
+ if (WIFSIGNALED (e))
+ printf ("died from signal %s\n", strsignal (WTERMSIG (e)));
+ else
+ puts ("did not terminate correctly");
+ exit (EXIT_FAILURE);
+ }
+ if (WEXITSTATUS (e) != 0)
+ FAIL_EXIT1 ("exit code %d", WEXITSTATUS (e));
+
+ return ret;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -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>
new file mode 100644
@@ -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>
new file mode 100644
@@ -0,0 +1,86 @@
+/* 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)
+{
+ bool ok = true;
+
+ puts ("in f");
+
+ if (TEST_STACK_ALIGN ())
+ ok = false;
+
+ return ok ? 0 : 1;
+}
+
+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);
+ if (!WIFEXITED (e))
+ {
+ if (WIFSIGNALED (e))
+ printf ("died from signal %s\n", strsignal (WTERMSIG (e)));
+ FAIL_EXIT1 ("process did not terminate correctly");
+ }
+
+ if (WEXITSTATUS (e) != 0)
+ FAIL_EXIT1 ("exit code %d", WEXITSTATUS (e));
+
+ return 0;
+}
+
+#include <support/test-driver.c>