1 | //===-- Tests for pthread_create ------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "src/pthread/pthread_attr_destroy.h" |
10 | #include "src/pthread/pthread_attr_getdetachstate.h" |
11 | #include "src/pthread/pthread_attr_getguardsize.h" |
12 | #include "src/pthread/pthread_attr_getstack.h" |
13 | #include "src/pthread/pthread_attr_getstacksize.h" |
14 | #include "src/pthread/pthread_attr_init.h" |
15 | #include "src/pthread/pthread_attr_setdetachstate.h" |
16 | #include "src/pthread/pthread_attr_setguardsize.h" |
17 | #include "src/pthread/pthread_attr_setstack.h" |
18 | #include "src/pthread/pthread_attr_setstacksize.h" |
19 | #include "src/pthread/pthread_create.h" |
20 | #include "src/pthread/pthread_join.h" |
21 | #include "src/pthread/pthread_self.h" |
22 | |
23 | #include "src/sys/mman/mmap.h" |
24 | #include "src/sys/mman/munmap.h" |
25 | #include "src/sys/random/getrandom.h" |
26 | |
27 | #include "src/__support/CPP/array.h" |
28 | #include "src/__support/CPP/atomic.h" |
29 | #include "src/__support/CPP/new.h" |
30 | #include "src/__support/threads/thread.h" |
31 | |
32 | #include "src/errno/libc_errno.h" |
33 | |
34 | #include "test/IntegrationTest/test.h" |
35 | |
36 | #include <linux/param.h> // For EXEC_PAGESIZE. |
37 | #include <pthread.h> |
38 | |
39 | struct TestThreadArgs { |
40 | pthread_attr_t attrs; |
41 | void *ret; |
42 | }; |
43 | static LIBC_NAMESPACE::AllocChecker global_ac; |
44 | static LIBC_NAMESPACE::cpp::Atomic<long> global_thr_count = 0; |
45 | |
46 | static void *successThread(void *Arg) { |
47 | pthread_t th = LIBC_NAMESPACE::pthread_self(); |
48 | auto *thread = reinterpret_cast<LIBC_NAMESPACE::Thread *>(&th); |
49 | |
50 | ASSERT_ERRNO_SUCCESS(); |
51 | ASSERT_TRUE(thread); |
52 | ASSERT_TRUE(thread->attrib); |
53 | |
54 | TestThreadArgs *th_arg = reinterpret_cast<TestThreadArgs *>(Arg); |
55 | pthread_attr_t *expec_attrs = &(th_arg->attrs); |
56 | void *ret = th_arg->ret; |
57 | |
58 | void *expec_stack; |
59 | size_t expec_stacksize, expec_guardsize, expec_stacksize2; |
60 | int expec_detached; |
61 | |
62 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_getstack(expec_attrs, &expec_stack, |
63 | &expec_stacksize), |
64 | 0); |
65 | ASSERT_ERRNO_SUCCESS(); |
66 | |
67 | ASSERT_EQ( |
68 | LIBC_NAMESPACE::pthread_attr_getstacksize(expec_attrs, &expec_stacksize2), |
69 | 0); |
70 | ASSERT_ERRNO_SUCCESS(); |
71 | |
72 | ASSERT_EQ( |
73 | LIBC_NAMESPACE::pthread_attr_getguardsize(expec_attrs, &expec_guardsize), |
74 | 0); |
75 | ASSERT_ERRNO_SUCCESS(); |
76 | |
77 | ASSERT_EQ( |
78 | LIBC_NAMESPACE::pthread_attr_getdetachstate(expec_attrs, &expec_detached), |
79 | 0); |
80 | ASSERT_ERRNO_SUCCESS(); |
81 | |
82 | ASSERT_EQ(expec_stacksize, expec_stacksize2); |
83 | |
84 | ASSERT_TRUE(thread->attrib->stack); |
85 | if (expec_stack != nullptr) { |
86 | ASSERT_EQ(thread->attrib->stack, expec_stack); |
87 | } else { |
88 | ASSERT_EQ(reinterpret_cast<uintptr_t>(thread->attrib->stack) % |
89 | EXEC_PAGESIZE, |
90 | static_cast<uintptr_t>(0)); |
91 | expec_stacksize = (expec_stacksize + EXEC_PAGESIZE - 1) & (-EXEC_PAGESIZE); |
92 | } |
93 | |
94 | ASSERT_TRUE(expec_stacksize); |
95 | ASSERT_EQ(thread->attrib->stacksize, expec_stacksize); |
96 | ASSERT_EQ(thread->attrib->guardsize, expec_guardsize); |
97 | |
98 | ASSERT_EQ(expec_detached == PTHREAD_CREATE_JOINABLE, |
99 | thread->attrib->detach_state.load() == |
100 | static_cast<uint32_t>(LIBC_NAMESPACE::DetachState::JOINABLE)); |
101 | ASSERT_EQ(expec_detached == PTHREAD_CREATE_DETACHED, |
102 | thread->attrib->detach_state.load() == |
103 | static_cast<uint32_t>(LIBC_NAMESPACE::DetachState::DETACHED)); |
104 | |
105 | { |
106 | // Allocate some bytes on the stack on most of the stack and make sure we |
107 | // have read/write permissions on the memory. |
108 | size_t test_stacksize = expec_stacksize - 1024; |
109 | volatile uint8_t *bytes_on_stack = |
110 | (volatile uint8_t *)__builtin_alloca(test_stacksize); |
111 | |
112 | for (size_t I = 0; I < test_stacksize; ++I) { |
113 | // Write permissions |
114 | bytes_on_stack[I] = static_cast<uint8_t>(I); |
115 | } |
116 | |
117 | for (size_t I = 0; I < test_stacksize; ++I) { |
118 | // Read/write permissions |
119 | bytes_on_stack[I] += static_cast<uint8_t>(I); |
120 | } |
121 | } |
122 | |
123 | // TODO: If guardsize != 0 && expec_stack == nullptr we should confirm that |
124 | // [stack - expec_guardsize, stack) is both mapped and has PROT_NONE |
125 | // permissions. Maybe we can read from /proc/{self}/map? |
126 | |
127 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(expec_attrs), 0); |
128 | ASSERT_ERRNO_SUCCESS(); |
129 | |
130 | // Arg is malloced, so free. |
131 | delete th_arg; |
132 | global_thr_count.fetch_sub(decrement: 1); |
133 | return ret; |
134 | } |
135 | |
136 | static void run_success_config(int detachstate, size_t guardsize, |
137 | size_t stacksize, bool customstack) { |
138 | |
139 | TestThreadArgs *th_arg = new (global_ac) TestThreadArgs{}; |
140 | pthread_attr_t *attr = &(th_arg->attrs); |
141 | |
142 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_init(attr), 0); |
143 | ASSERT_ERRNO_SUCCESS(); |
144 | |
145 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_setdetachstate(attr, detachstate), 0); |
146 | ASSERT_ERRNO_SUCCESS(); |
147 | |
148 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_setguardsize(attr, guardsize), 0); |
149 | ASSERT_ERRNO_SUCCESS(); |
150 | |
151 | void *Stack = nullptr; |
152 | if (customstack) { |
153 | Stack = LIBC_NAMESPACE::mmap(addr: nullptr, size: stacksize, PROT_READ | PROT_WRITE, |
154 | MAP_PRIVATE | MAP_ANONYMOUS, fd: -1, offset: 0); |
155 | ASSERT_NE(Stack, MAP_FAILED); |
156 | ASSERT_NE(Stack, static_cast<void *>(nullptr)); |
157 | ASSERT_ERRNO_SUCCESS(); |
158 | |
159 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_setstack(attr, Stack, stacksize), 0); |
160 | ASSERT_ERRNO_SUCCESS(); |
161 | } else { |
162 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_setstacksize(attr, stacksize), 0); |
163 | ASSERT_ERRNO_SUCCESS(); |
164 | } |
165 | |
166 | void *expec_ret = nullptr; |
167 | if (detachstate == PTHREAD_CREATE_JOINABLE) { |
168 | ASSERT_EQ(LIBC_NAMESPACE::getrandom(&expec_ret, sizeof(expec_ret), 0), |
169 | static_cast<ssize_t>(sizeof(expec_ret))); |
170 | ASSERT_ERRNO_SUCCESS(); |
171 | } |
172 | |
173 | th_arg->ret = expec_ret; |
174 | global_thr_count.fetch_add(increment: 1); |
175 | |
176 | pthread_t tid; |
177 | // th_arg and attr are cleanup by the thread. |
178 | ASSERT_EQ(LIBC_NAMESPACE::pthread_create(&tid, attr, successThread, |
179 | reinterpret_cast<void *>(th_arg)), |
180 | 0); |
181 | ASSERT_ERRNO_SUCCESS(); |
182 | |
183 | if (detachstate == PTHREAD_CREATE_JOINABLE) { |
184 | void *th_ret; |
185 | ASSERT_EQ(LIBC_NAMESPACE::pthread_join(tid, &th_ret), 0); |
186 | ASSERT_ERRNO_SUCCESS(); |
187 | ASSERT_EQ(th_ret, expec_ret); |
188 | |
189 | if (customstack) { |
190 | ASSERT_EQ(LIBC_NAMESPACE::munmap(Stack, stacksize), 0); |
191 | ASSERT_ERRNO_SUCCESS(); |
192 | } |
193 | } else { |
194 | ASSERT_FALSE(customstack); |
195 | } |
196 | } |
197 | |
198 | static void run_success_tests() { |
199 | |
200 | // Test parameters |
201 | using LIBC_NAMESPACE::cpp::array; |
202 | |
203 | array<int, 2> detachstates = {PTHREAD_CREATE_DETACHED, |
204 | PTHREAD_CREATE_JOINABLE}; |
205 | array<size_t, 4> guardsizes = {0, EXEC_PAGESIZE, 2 * EXEC_PAGESIZE, |
206 | 123 * EXEC_PAGESIZE}; |
207 | array<size_t, 6> stacksizes = {PTHREAD_STACK_MIN, |
208 | PTHREAD_STACK_MIN + 16, |
209 | (1 << 16) - EXEC_PAGESIZE / 2, |
210 | (1 << 16) + EXEC_PAGESIZE / 2, |
211 | 1234560, |
212 | 1234560 * 2}; |
213 | array<bool, 2> customstacks = {true, false}; |
214 | |
215 | for (int detachstate : detachstates) { |
216 | for (size_t guardsize : guardsizes) { |
217 | for (size_t stacksize : stacksizes) { |
218 | for (bool customstack : customstacks) { |
219 | if (customstack) { |
220 | |
221 | // TODO: figure out how to test a user allocated stack |
222 | // along with detached pthread safely. We can't let the |
223 | // thread deallocate it owns stack for obvious |
224 | // reasons. And there doesn't appear to be a good way to |
225 | // check if a detached thread has exited. NB: It's racey to just |
226 | // wait for an atomic variable at the end of the thread function as |
227 | // internal thread cleanup functions continue to use its stack. |
228 | // Maybe an `atexit` handler would work. |
229 | if (detachstate == PTHREAD_CREATE_DETACHED) |
230 | continue; |
231 | |
232 | // Guardsize has no meaning with user provided stack. |
233 | if (guardsize) |
234 | continue; |
235 | |
236 | run_success_config(detachstate, guardsize, stacksize, customstack); |
237 | } |
238 | } |
239 | } |
240 | } |
241 | } |
242 | |
243 | // Wait for detached threads to finish testing (this is not gurantee they will |
244 | // have cleaned up) |
245 | while (global_thr_count.load()) |
246 | ; |
247 | } |
248 | |
249 | static void *failure_thread(void *) { |
250 | // Should be unreachable; |
251 | ASSERT_TRUE(false); |
252 | return nullptr; |
253 | } |
254 | |
255 | static void create_and_check_failure_thread(pthread_attr_t *attr) { |
256 | pthread_t tid; |
257 | int result = |
258 | LIBC_NAMESPACE::pthread_create(&tid, attr, failure_thread, nullptr); |
259 | // EINVAL if we caught on overflow or something of that nature. EAGAIN if it |
260 | // was just really larger we failed mmap. |
261 | ASSERT_TRUE(result == EINVAL || result == EAGAIN); |
262 | // pthread_create should NOT set errno on error |
263 | ASSERT_ERRNO_SUCCESS(); |
264 | |
265 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_destroy(attr), 0); |
266 | ASSERT_ERRNO_SUCCESS(); |
267 | } |
268 | |
269 | static void run_failure_config(size_t guardsize, size_t stacksize) { |
270 | pthread_attr_t attr; |
271 | guardsize &= -EXEC_PAGESIZE; |
272 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_init(&attr), 0); |
273 | ASSERT_ERRNO_SUCCESS(); |
274 | |
275 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_setguardsize(&attr, guardsize), 0); |
276 | ASSERT_ERRNO_SUCCESS(); |
277 | |
278 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_setstacksize(&attr, stacksize), 0); |
279 | ASSERT_ERRNO_SUCCESS(); |
280 | |
281 | create_and_check_failure_thread(attr: &attr); |
282 | } |
283 | |
284 | static void run_failure_tests() { |
285 | // Just some tests where the user sets "valid" parameters but they fail |
286 | // (overflow or too large to allocate). |
287 | run_failure_config(SIZE_MAX, PTHREAD_STACK_MIN); |
288 | run_failure_config(SIZE_MAX - PTHREAD_STACK_MIN, PTHREAD_STACK_MIN * 2); |
289 | run_failure_config(PTHREAD_STACK_MIN, SIZE_MAX); |
290 | run_failure_config(PTHREAD_STACK_MIN, SIZE_MAX - PTHREAD_STACK_MIN); |
291 | run_failure_config(SIZE_MAX / 2, SIZE_MAX / 2); |
292 | run_failure_config(guardsize: 3 * (SIZE_MAX / 4), SIZE_MAX / 4); |
293 | run_failure_config(SIZE_MAX / 2 + 1234, SIZE_MAX / 2); |
294 | |
295 | // Test invalid parameters that are impossible to obtain via the |
296 | // `pthread_attr_set*` API. Still test that this not entirely unlikely |
297 | // initialization doesn't cause any issues. Basically we wan't to make sure |
298 | // that `pthread_create` properly checks for input validity and doesn't rely |
299 | // on the `pthread_attr_set*` API. |
300 | pthread_attr_t attr; |
301 | |
302 | // Stacksize too small. |
303 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_init(&attr), 0); |
304 | ASSERT_ERRNO_SUCCESS(); |
305 | attr.__stacksize = PTHREAD_STACK_MIN - 16; |
306 | create_and_check_failure_thread(attr: &attr); |
307 | |
308 | // Stack misaligned. |
309 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_init(&attr), 0); |
310 | ASSERT_ERRNO_SUCCESS(); |
311 | attr.__stack = reinterpret_cast<void *>(1); |
312 | create_and_check_failure_thread(attr: &attr); |
313 | |
314 | // Stack + stacksize misaligned. |
315 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_init(&attr), 0); |
316 | ASSERT_ERRNO_SUCCESS(); |
317 | attr.__stacksize = PTHREAD_STACK_MIN + 1; |
318 | attr.__stack = reinterpret_cast<void *>(16); |
319 | create_and_check_failure_thread(attr: &attr); |
320 | |
321 | // Guardsize misaligned. |
322 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_init(&attr), 0); |
323 | ASSERT_ERRNO_SUCCESS(); |
324 | attr.__guardsize = EXEC_PAGESIZE / 2; |
325 | create_and_check_failure_thread(attr: &attr); |
326 | |
327 | // Detachstate is unknown. |
328 | ASSERT_EQ(LIBC_NAMESPACE::pthread_attr_init(&attr), 0); |
329 | ASSERT_ERRNO_SUCCESS(); |
330 | attr.__detachstate = -1; |
331 | create_and_check_failure_thread(attr: &attr); |
332 | } |
333 | |
334 | TEST_MAIN() { |
335 | LIBC_NAMESPACE::libc_errno = 0; |
336 | run_success_tests(); |
337 | run_failure_tests(); |
338 | return 0; |
339 | } |
340 | |