unix: don't allow too small thread stack size (#3423)

uv_thread_create_ex() lets you set a stack size that is smaller than is
safe. It enforces a lower bound of PTHREAD_STACK_MIN (when that constant
is defined) but with musl libc that's still too small to receive signals
on.

Put the lower bound at 8192 or PTHREAD_STACK_MIN, whichever is greater.
The same restriction was already in place for the _default_ stack size.
This commit is contained in:
Ben Noordhuis 2022-01-17 09:36:26 +01:00 committed by GitHub
parent d5ed7f1256
commit b5fa965bcb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 32 deletions

View File

@ -162,25 +162,7 @@ void uv_barrier_destroy(uv_barrier_t* barrier) {
#endif #endif
/* On MacOS, threads other than the main thread are created with a reduced /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is
* stack size by default. Adjust to RLIMIT_STACK aligned to the page size.
*
* On Linux, threads created by musl have a much smaller stack than threads
* created by glibc (80 vs. 2048 or 4096 kB.) Follow glibc for consistency.
*/
size_t uv__thread_stack_size(void) {
#if defined(__APPLE__) || defined(__linux__)
struct rlimit lim;
/* getrlimit() can fail on some aarch64 systems due to a glibc bug where
* the system call wrapper invokes the wrong system call. Don't treat
* that as fatal, just use the default stack size instead.
*/
if (0 == getrlimit(RLIMIT_STACK, &lim) && lim.rlim_cur != RLIM_INFINITY) {
/* pthread_attr_setstacksize() expects page-aligned values. */
lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
/* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is
* too small to safely receive signals on. * too small to safely receive signals on.
* *
* Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has
@ -191,12 +173,22 @@ size_t uv__thread_stack_size(void) {
* is between 28 and 133 KB when compiling against glibc, depending * is between 28 and 133 KB when compiling against glibc, depending
* on the architecture. * on the architecture.
*/ */
if (lim.rlim_cur >= 8192) static size_t uv__min_stack_size(void) {
if (lim.rlim_cur >= PTHREAD_STACK_MIN) static const size_t min = 8192;
return lim.rlim_cur;
}
#endif
#ifdef PTHREAD_STACK_MIN /* Not defined on NetBSD. */
if (min < (size_t) PTHREAD_STACK_MIN)
return PTHREAD_STACK_MIN;
#endif /* PTHREAD_STACK_MIN */
return min;
}
/* On Linux, threads created by musl have a much smaller stack than threads
* created by glibc (80 vs. 2048 or 4096 kB.) Follow glibc for consistency.
*/
static size_t uv__default_stack_size(void) {
#if !defined(__linux__) #if !defined(__linux__)
return 0; return 0;
#elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__) #elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__)
@ -207,6 +199,34 @@ size_t uv__thread_stack_size(void) {
} }
/* On MacOS, threads other than the main thread are created with a reduced
* stack size by default. Adjust to RLIMIT_STACK aligned to the page size.
*/
size_t uv__thread_stack_size(void) {
#if defined(__APPLE__) || defined(__linux__)
struct rlimit lim;
/* getrlimit() can fail on some aarch64 systems due to a glibc bug where
* the system call wrapper invokes the wrong system call. Don't treat
* that as fatal, just use the default stack size instead.
*/
if (getrlimit(RLIMIT_STACK, &lim))
return uv__default_stack_size();
if (lim.rlim_cur == RLIM_INFINITY)
return uv__default_stack_size();
/* pthread_attr_setstacksize() expects page-aligned values. */
lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize();
if (lim.rlim_cur >= (rlim_t) uv__min_stack_size())
return lim.rlim_cur;
#endif
return uv__default_stack_size();
}
int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) {
uv_thread_options_t params; uv_thread_options_t params;
params.flags = UV_THREAD_NO_FLAGS; params.flags = UV_THREAD_NO_FLAGS;
@ -222,6 +242,7 @@ int uv_thread_create_ex(uv_thread_t* tid,
pthread_attr_t attr_storage; pthread_attr_t attr_storage;
size_t pagesize; size_t pagesize;
size_t stack_size; size_t stack_size;
size_t min_stack_size;
/* Used to squelch a -Wcast-function-type warning. */ /* Used to squelch a -Wcast-function-type warning. */
union { union {
@ -239,10 +260,9 @@ int uv_thread_create_ex(uv_thread_t* tid,
pagesize = (size_t)getpagesize(); pagesize = (size_t)getpagesize();
/* Round up to the nearest page boundary. */ /* Round up to the nearest page boundary. */
stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1); stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1);
#ifdef PTHREAD_STACK_MIN min_stack_size = uv__min_stack_size();
if (stack_size < PTHREAD_STACK_MIN) if (stack_size < min_stack_size)
stack_size = PTHREAD_STACK_MIN; stack_size = min_stack_size;
#endif
} }
if (stack_size > 0) { if (stack_size > 0) {

View File

@ -273,6 +273,11 @@ TEST_IMPL(thread_stack_size_explicit) {
thread_check_stack, &options)); thread_check_stack, &options));
ASSERT(0 == uv_thread_join(&thread)); ASSERT(0 == uv_thread_join(&thread));
options.stack_size = 42;
ASSERT(0 == uv_thread_create_ex(&thread, &options,
thread_check_stack, &options));
ASSERT(0 == uv_thread_join(&thread));
#ifdef PTHREAD_STACK_MIN #ifdef PTHREAD_STACK_MIN
options.stack_size = PTHREAD_STACK_MIN - 42; /* unaligned size */ options.stack_size = PTHREAD_STACK_MIN - 42; /* unaligned size */
ASSERT(0 == uv_thread_create_ex(&thread, &options, ASSERT(0 == uv_thread_create_ex(&thread, &options,