brintos

brintos / llvm-project-archived public Read only

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