| 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/__support/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(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(nullptr, stacksize, PROT_READ | PROT_WRITE, |
| 154 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 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(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(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_errno = 0; |
| 336 | run_success_tests(); |
| 337 | run_failure_tests(); |
| 338 | return 0; |
| 339 | } |
| 340 | |