xref: /llvm-project/libc/test/integration/src/pthread/pthread_create_test.cpp (revision 6a185718d4a29d17a389d0c50419e7e36ac3c866)
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 __llvm_libc::AllocChecker global_ac;
44 static __llvm_libc::cpp::Atomic<long> global_thr_count = 0;
45 
46 static void *successThread(void *Arg) {
47   pthread_t th = __llvm_libc::pthread_self();
48   auto *thread = reinterpret_cast<__llvm_libc::Thread *>(&th);
49 
50   ASSERT_EQ(libc_errno, 0);
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(__llvm_libc::pthread_attr_getstack(expec_attrs, &expec_stack,
63                                                &expec_stacksize),
64             0);
65   ASSERT_EQ(libc_errno, 0);
66 
67   ASSERT_EQ(
68       __llvm_libc::pthread_attr_getstacksize(expec_attrs, &expec_stacksize2),
69       0);
70   ASSERT_EQ(libc_errno, 0);
71 
72   ASSERT_EQ(
73       __llvm_libc::pthread_attr_getguardsize(expec_attrs, &expec_guardsize), 0);
74   ASSERT_EQ(libc_errno, 0);
75 
76   ASSERT_EQ(
77       __llvm_libc::pthread_attr_getdetachstate(expec_attrs, &expec_detached),
78       0);
79   ASSERT_EQ(libc_errno, 0);
80 
81   ASSERT_EQ(expec_stacksize, expec_stacksize2);
82 
83   ASSERT_TRUE(thread->attrib->stack);
84   if (expec_stack != nullptr) {
85     ASSERT_EQ(thread->attrib->stack, expec_stack);
86   } else {
87     ASSERT_EQ(reinterpret_cast<uintptr_t>(thread->attrib->stack) %
88                   EXEC_PAGESIZE,
89               static_cast<uintptr_t>(0));
90     expec_stacksize = (expec_stacksize + EXEC_PAGESIZE - 1) & (-EXEC_PAGESIZE);
91   }
92 
93   ASSERT_TRUE(expec_stacksize);
94   ASSERT_EQ(thread->attrib->stacksize, expec_stacksize);
95   ASSERT_EQ(thread->attrib->guardsize, expec_guardsize);
96 
97   ASSERT_EQ(expec_detached == PTHREAD_CREATE_JOINABLE,
98             thread->attrib->detach_state.load() ==
99                 static_cast<uint32_t>(__llvm_libc::DetachState::JOINABLE));
100   ASSERT_EQ(expec_detached == PTHREAD_CREATE_DETACHED,
101             thread->attrib->detach_state.load() ==
102                 static_cast<uint32_t>(__llvm_libc::DetachState::DETACHED));
103 
104   {
105     // Allocate some bytes on the stack on most of the stack and make sure we
106     // have read/write permissions on the memory. -EXEC_PAGESIZE / 2 just as a
107     // buffer so we don't go beyond our limits (no nested function calls / not
108     // much other data on the stack so should be enough).
109     size_t test_stacksize = expec_stacksize - EXEC_PAGESIZE / 2;
110     volatile uint8_t *bytes_on_stack =
111         (volatile uint8_t *)__builtin_alloca(test_stacksize);
112 
113     for (size_t I = 0; I < test_stacksize; ++I) {
114       // Write permissions
115       bytes_on_stack[I] = static_cast<uint8_t>(I);
116     }
117 
118     for (size_t I = 0; I < test_stacksize; ++I) {
119       // Read/write permissions
120       bytes_on_stack[I] += static_cast<uint8_t>(I);
121     }
122   }
123 
124   // TODO: If guardsize != 0 && expec_stack == nullptr we should confirm that
125   // [stack - expec_guardsize, stack) is both mapped and has PROT_NONE
126   // permissions. Maybe we can read from /proc/{self}/map?
127 
128   ASSERT_EQ(__llvm_libc::pthread_attr_destroy(expec_attrs), 0);
129   ASSERT_EQ(libc_errno, 0);
130 
131   // Arg is malloced, so free.
132   delete th_arg;
133   global_thr_count.fetch_sub(1);
134   return ret;
135 }
136 
137 static void run_success_config(int detachstate, size_t guardsize,
138                                size_t stacksize, bool customstack) {
139 
140   TestThreadArgs *th_arg = new (global_ac) TestThreadArgs{};
141   pthread_attr_t *attr = &(th_arg->attrs);
142 
143   ASSERT_EQ(__llvm_libc::pthread_attr_init(attr), 0);
144   ASSERT_EQ(libc_errno, 0);
145 
146   ASSERT_EQ(__llvm_libc::pthread_attr_setdetachstate(attr, detachstate), 0);
147   ASSERT_EQ(libc_errno, 0);
148 
149   ASSERT_EQ(__llvm_libc::pthread_attr_setguardsize(attr, guardsize), 0);
150   ASSERT_EQ(libc_errno, 0);
151 
152   void *Stack = nullptr;
153   if (customstack) {
154     Stack = __llvm_libc::mmap(nullptr, stacksize, PROT_READ | PROT_WRITE,
155                               MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
156     ASSERT_NE(Stack, MAP_FAILED);
157     ASSERT_NE(Stack, static_cast<void *>(nullptr));
158     ASSERT_EQ(libc_errno, 0);
159 
160     ASSERT_EQ(__llvm_libc::pthread_attr_setstack(attr, Stack, stacksize), 0);
161     ASSERT_EQ(libc_errno, 0);
162   } else {
163     ASSERT_EQ(__llvm_libc::pthread_attr_setstacksize(attr, stacksize), 0);
164     ASSERT_EQ(libc_errno, 0);
165   }
166 
167   void *expec_ret = nullptr;
168   if (detachstate == PTHREAD_CREATE_JOINABLE) {
169     ASSERT_EQ(__llvm_libc::getrandom(&expec_ret, sizeof(expec_ret), 0),
170               static_cast<ssize_t>(sizeof(expec_ret)));
171     ASSERT_EQ(libc_errno, 0);
172   }
173 
174   th_arg->ret = expec_ret;
175   global_thr_count.fetch_add(1);
176 
177   pthread_t tid;
178   // th_arg and attr are cleanup by the thread.
179   ASSERT_EQ(__llvm_libc::pthread_create(&tid, attr, successThread,
180                                         reinterpret_cast<void *>(th_arg)),
181             0);
182   ASSERT_EQ(libc_errno, 0);
183 
184   if (detachstate == PTHREAD_CREATE_JOINABLE) {
185     void *th_ret;
186     ASSERT_EQ(__llvm_libc::pthread_join(tid, &th_ret), 0);
187     ASSERT_EQ(libc_errno, 0);
188     ASSERT_EQ(th_ret, expec_ret);
189 
190     if (customstack) {
191       ASSERT_EQ(__llvm_libc::munmap(Stack, stacksize), 0);
192       ASSERT_EQ(libc_errno, 0);
193     }
194   } else {
195     ASSERT_FALSE(customstack);
196   }
197 }
198 
199 static void run_success_tests() {
200 
201   // Test parameters
202   using __llvm_libc::cpp::array;
203 
204   array<int, 2> detachstates = {PTHREAD_CREATE_DETACHED,
205                                 PTHREAD_CREATE_JOINABLE};
206   array<size_t, 4> guardsizes = {0, EXEC_PAGESIZE, 2 * EXEC_PAGESIZE,
207                                  123 * EXEC_PAGESIZE};
208   array<size_t, 6> stacksizes = {PTHREAD_STACK_MIN,
209                                  PTHREAD_STACK_MIN + 16,
210                                  (1 << 16) - EXEC_PAGESIZE / 2,
211                                  (1 << 16) + EXEC_PAGESIZE / 2,
212                                  1234560,
213                                  1234560 * 2};
214   array<bool, 2> customstacks = {true, false};
215 
216   for (int detachstate : detachstates) {
217     for (size_t guardsize : guardsizes) {
218       for (size_t stacksize : stacksizes) {
219         for (bool customstack : customstacks) {
220           if (customstack) {
221 
222             // TODO: figure out how to test a user allocated stack
223             // along with detached pthread safely. We can't let the
224             // thread deallocate it owns stack for obvious
225             // reasons. And there doesn't appear to be a good way to
226             // check if a detached thread has exited. NB: It's racey to just
227             // wait for an atomic variable at the end of the thread function as
228             // internal thread cleanup functions continue to use its stack.
229             // Maybe an `atexit` handler would work.
230             if (detachstate == PTHREAD_CREATE_DETACHED)
231               continue;
232 
233             // Guardsize has no meaning with user provided stack.
234             if (guardsize)
235               continue;
236 
237             run_success_config(detachstate, guardsize, stacksize, customstack);
238           }
239         }
240       }
241     }
242   }
243 
244   // Wait for detached threads to finish testing (this is not gurantee they will
245   // have cleaned up)
246   while (global_thr_count.load())
247     ;
248 }
249 
250 static void *failure_thread(void *) {
251   // Should be unreachable;
252   ASSERT_TRUE(false);
253   return nullptr;
254 }
255 
256 static void create_and_check_failure_thread(pthread_attr_t *attr) {
257   pthread_t tid;
258   int result = __llvm_libc::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_EQ(libc_errno, 0);
264 
265   ASSERT_EQ(__llvm_libc::pthread_attr_destroy(attr), 0);
266   ASSERT_EQ(libc_errno, 0);
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(__llvm_libc::pthread_attr_init(&attr), 0);
273   ASSERT_EQ(libc_errno, 0);
274 
275   ASSERT_EQ(__llvm_libc::pthread_attr_setguardsize(&attr, guardsize), 0);
276   ASSERT_EQ(libc_errno, 0);
277 
278   ASSERT_EQ(__llvm_libc::pthread_attr_setstacksize(&attr, stacksize), 0);
279   ASSERT_EQ(libc_errno, 0);
280 
281   create_and_check_failure_thread(&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(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(__llvm_libc::pthread_attr_init(&attr), 0);
304   ASSERT_EQ(libc_errno, 0);
305   attr.__stacksize = PTHREAD_STACK_MIN - 16;
306   create_and_check_failure_thread(&attr);
307 
308   // Stack misaligned.
309   ASSERT_EQ(__llvm_libc::pthread_attr_init(&attr), 0);
310   ASSERT_EQ(libc_errno, 0);
311   attr.__stack = reinterpret_cast<void *>(1);
312   create_and_check_failure_thread(&attr);
313 
314   // Stack + stacksize misaligned.
315   ASSERT_EQ(__llvm_libc::pthread_attr_init(&attr), 0);
316   ASSERT_EQ(libc_errno, 0);
317   attr.__stacksize = PTHREAD_STACK_MIN + 1;
318   attr.__stack = reinterpret_cast<void *>(16);
319   create_and_check_failure_thread(&attr);
320 
321   // Guardsize misaligned.
322   ASSERT_EQ(__llvm_libc::pthread_attr_init(&attr), 0);
323   ASSERT_EQ(libc_errno, 0);
324   attr.__guardsize = EXEC_PAGESIZE / 2;
325   create_and_check_failure_thread(&attr);
326 
327   // Detachstate is unknown.
328   ASSERT_EQ(__llvm_libc::pthread_attr_init(&attr), 0);
329   ASSERT_EQ(libc_errno, 0);
330   attr.__detachstate = -1;
331   create_and_check_failure_thread(&attr);
332 }
333 
334 TEST_MAIN() {
335   libc_errno = 0;
336   run_success_tests();
337   run_failure_tests();
338   return 0;
339 }
340