1 | /* Check if clone (CLONE_THREAD) does not call exit_group (BZ #21512) |
2 | Copyright (C) 2017-2022 Free Software Foundation, Inc. |
3 | This file is part of the GNU C Library. |
4 | |
5 | The GNU C Library is free software; you can redistribute it and/or |
6 | modify it under the terms of the GNU Lesser General Public |
7 | License as published by the Free Software Foundation; either |
8 | version 2.1 of the License, or (at your option) any later version. |
9 | |
10 | The GNU C Library is distributed in the hope that it will be useful, |
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | Lesser General Public License for more details. |
14 | |
15 | You should have received a copy of the GNU Lesser General Public |
16 | License along with the GNU C Library; if not, see |
17 | <https://www.gnu.org/licenses/>. */ |
18 | |
19 | #include <string.h> |
20 | #include <sched.h> |
21 | #include <signal.h> |
22 | #include <unistd.h> |
23 | #include <sys/syscall.h> |
24 | #include <sys/wait.h> |
25 | #include <sys/types.h> |
26 | #include <linux/futex.h> |
27 | |
28 | #include <stackinfo.h> /* For _STACK_GROWS_{UP,DOWN}. */ |
29 | #include <support/check.h> |
30 | #include <stdatomic.h> |
31 | |
32 | /* Test if clone call with CLONE_THREAD does not call exit_group. The 'f' |
33 | function returns '1', which will be used by clone thread to call the |
34 | 'exit' syscall directly. If _exit is used instead, exit_group will be |
35 | used and thus the thread group will finish with return value of '1' |
36 | (where '2' from main thread is expected. */ |
37 | |
38 | static int |
39 | f (void *a) |
40 | { |
41 | return 1; |
42 | } |
43 | |
44 | /* Futex wait for TID argument, similar to pthread_join internal |
45 | implementation. */ |
46 | #define wait_tid(ctid_ptr, ctid_val) \ |
47 | do { \ |
48 | __typeof (*(ctid_ptr)) __tid; \ |
49 | /* We need acquire MO here so that we synchronize with the \ |
50 | kernel's store to 0 when the clone terminates. */ \ |
51 | while ((__tid = atomic_load_explicit (ctid_ptr, \ |
52 | memory_order_acquire)) != 0) \ |
53 | futex_wait (ctid_ptr, ctid_val); \ |
54 | } while (0) |
55 | |
56 | static inline int |
57 | futex_wait (int *futexp, int val) |
58 | { |
59 | #ifdef __NR_futex |
60 | return syscall (__NR_futex, futexp, FUTEX_WAIT, val); |
61 | #else |
62 | return syscall (__NR_futex_time64, futexp, FUTEX_WAIT, val); |
63 | #endif |
64 | } |
65 | |
66 | static int |
67 | do_test (void) |
68 | { |
69 | char st[1024] __attribute__ ((aligned)); |
70 | int clone_flags = CLONE_THREAD; |
71 | /* Minimum required flags to used along with CLONE_THREAD. */ |
72 | clone_flags |= CLONE_VM | CLONE_SIGHAND; |
73 | /* We will used ctid to call on futex to wait for thread exit. */ |
74 | clone_flags |= CLONE_CHILD_CLEARTID; |
75 | /* Initialize with a known value. ctid is set to zero by the kernel after the |
76 | cloned thread has exited. */ |
77 | #define CTID_INIT_VAL 1 |
78 | pid_t ctid = CTID_INIT_VAL; |
79 | pid_t tid; |
80 | |
81 | #ifdef __ia64__ |
82 | extern int __clone2 (int (*__fn) (void *__arg), void *__child_stack_base, |
83 | size_t __child_stack_size, int __flags, |
84 | void *__arg, ...); |
85 | tid = __clone2 (f, st, sizeof (st), clone_flags, NULL, /* ptid */ NULL, |
86 | /* tls */ NULL, &ctid); |
87 | #else |
88 | #if _STACK_GROWS_DOWN |
89 | tid = clone (fn: f, child_stack: st + sizeof (st), flags: clone_flags, NULL, /* ptid */ NULL, |
90 | /* tls */ NULL, &ctid); |
91 | #elif _STACK_GROWS_UP |
92 | tid = clone (f, st, clone_flags, NULL, /* ptid */ NULL, /* tls */ NULL, |
93 | &ctid); |
94 | #else |
95 | #error "Define either _STACK_GROWS_DOWN or _STACK_GROWS_UP" |
96 | #endif |
97 | #endif |
98 | if (tid == -1) |
99 | FAIL_EXIT1 ("clone failed: %m" ); |
100 | |
101 | wait_tid (&ctid, CTID_INIT_VAL); |
102 | |
103 | return 2; |
104 | } |
105 | |
106 | #define EXPECTED_STATUS 2 |
107 | #include <support/test-driver.c> |
108 | |