Skip to content

Commit

Permalink
8229147: Linux os::create_thread() overcounts guardpage size with new…
Browse files Browse the repository at this point in the history
…er glibc (>=2.27)

Reviewed-by: mbaesken
Backport-of: 9ebcda2165c42e3f7b82a9ae8074badb69c0d270
  • Loading branch information
TheRealMDoerr committed Jun 23, 2023
1 parent dfe3555 commit 832707b
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 37 deletions.
97 changes: 62 additions & 35 deletions src/hotspot/os/linux/os_linux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -744,20 +744,14 @@ static void *thread_native_entry(Thread *thread) {
// As a workaround, we call a private but assumed-stable glibc function,
// __pthread_get_minstack() to obtain the minstack size and derive the
// static TLS size from it. We then increase the user requested stack
// size by this TLS size.
// size by this TLS size. The same function is used to determine whether
// adjustStackSizeForGuardPages() needs to be true.
//
// Due to compatibility concerns, this size adjustment is opt-in and
// controlled via AdjustStackSizeForTLS.
typedef size_t (*GetMinStack)(const pthread_attr_t *attr);

GetMinStack _get_minstack_func = NULL;

static void get_minstack_init() {
_get_minstack_func =
(GetMinStack)dlsym(RTLD_DEFAULT, "__pthread_get_minstack");
log_info(os, thread)("Lookup of __pthread_get_minstack %s",
_get_minstack_func == NULL ? "failed" : "succeeded");
}
GetMinStack _get_minstack_func = nullptr; // Initialized via os::init_2()

// Returns the size of the static TLS area glibc puts on thread stacks.
// The value is cached on first use, which occurs when the first thread
Expand All @@ -770,8 +764,8 @@ static size_t get_static_tls_area_size(const pthread_attr_t *attr) {

// Remove non-TLS area size included in minstack size returned
// by __pthread_get_minstack() to get the static TLS size.
// In glibc before 2.27, minstack size includes guard_size.
// In glibc 2.27 and later, guard_size is automatically added
// If adjustStackSizeForGuardPages() is true, minstack size includes
// guard_size. Otherwise guard_size is automatically added
// to the stack size by pthread_create and is no longer included
// in minstack size. In both cases, the guard_size is taken into
// account, so there is no need to adjust the result for that.
Expand Down Expand Up @@ -800,6 +794,42 @@ static size_t get_static_tls_area_size(const pthread_attr_t *attr) {
return tls_size;
}

// In glibc versions prior to 2.27 the guard size mechanism
// was not implemented properly. The POSIX standard requires adding
// the size of the guard pages to the stack size, instead glibc
// took the space out of 'stacksize'. Thus we need to adapt the requested
// stack_size by the size of the guard pages to mimic proper behaviour.
// The fix in glibc 2.27 has now been backported to numerous earlier
// glibc versions so we need to do a dynamic runtime check.
static bool _adjustStackSizeForGuardPages = true;
bool os::Linux::adjustStackSizeForGuardPages() {
return _adjustStackSizeForGuardPages;
}

#ifdef __GLIBC__
static void init_adjust_stacksize_for_guard_pages() {
assert(_get_minstack_func == nullptr, "initialization error");
_get_minstack_func =(GetMinStack)dlsym(RTLD_DEFAULT, "__pthread_get_minstack");
log_info(os, thread)("Lookup of __pthread_get_minstack %s",
_get_minstack_func == nullptr ? "failed" : "succeeded");

if (_get_minstack_func != nullptr) {
pthread_attr_t attr;
pthread_attr_init(&attr);
size_t min_stack = _get_minstack_func(&attr);
size_t guard = 16 * K; // Actual value doesn't matter as it is not examined
pthread_attr_setguardsize(&attr, guard);
size_t min_stack2 = _get_minstack_func(&attr);
pthread_attr_destroy(&attr);
// If the minimum stack size changed when we added the guard page space
// then we need to perform the adjustment.
_adjustStackSizeForGuardPages = (min_stack2 != min_stack);
log_info(os)("Glibc stack size guard page adjustment is %sneeded",
_adjustStackSizeForGuardPages ? "" : "not ");
}
}
#endif // GLIBC

bool os::create_thread(Thread* thread, ThreadType thr_type,
size_t req_stack_size) {
assert(thread->osthread() == NULL, "caller responsible");
Expand All @@ -825,23 +855,18 @@ bool os::create_thread(Thread* thread, ThreadType thr_type,

// Calculate stack size if it's not specified by caller.
size_t stack_size = os::Posix::get_initial_stack_size(thr_type, req_stack_size);
// In glibc versions prior to 2.7 the guard size mechanism
// is not implemented properly. The posix standard requires adding
// the size of the guard pages to the stack size, instead Linux
// takes the space out of 'stacksize'. Thus we adapt the requested
// stack_size by the size of the guard pages to mimick proper
// behaviour. However, be careful not to end up with a size
// of zero due to overflow. Don't add the guard page in that case.
size_t guard_size = os::Linux::default_guard_size(thr_type);
// Configure glibc guard page. Must happen before calling
// get_static_tls_area_size(), which uses the guard_size.
pthread_attr_setguardsize(&attr, guard_size);

// Apply stack size adjustments if needed. However, be careful not to end up
// with a size of zero due to overflow. Don't add the adjustment in that case.
size_t stack_adjust_size = 0;
if (AdjustStackSizeForTLS) {
// Adjust the stack_size for on-stack TLS - see get_static_tls_area_size().
stack_adjust_size += get_static_tls_area_size(&attr);
} else {
} else if (os::Linux::adjustStackSizeForGuardPages()) {
stack_adjust_size += guard_size;
}

Expand Down Expand Up @@ -995,7 +1020,7 @@ bool os::create_attached_thread(JavaThread* thread) {
log_info(os, thread)("Thread attached (tid: " UINTX_FORMAT ", pthread id: " UINTX_FORMAT
", stack: " PTR_FORMAT " - " PTR_FORMAT " (" SIZE_FORMAT "K) ).",
os::current_thread_id(), (uintx) pthread_self(),
p2i(thread->stack_base()), p2i(thread->stack_end()), thread->stack_size());
p2i(thread->stack_base()), p2i(thread->stack_end()), thread->stack_size() / K);

return true;
}
Expand Down Expand Up @@ -1335,7 +1360,7 @@ void os::Linux::fast_thread_clock_init() {
// Note, that some kernels may support the current thread
// clock (CLOCK_THREAD_CPUTIME_ID) but not the clocks
// returned by the pthread_getcpuclockid().
// If the fast Posix clocks are supported then the clock_getres()
// If the fast POSIX clocks are supported then the clock_getres()
// must return at least tp.tv_sec == 0 which means a resolution
// better than 1 sec. This is extra check for reliability.

Expand Down Expand Up @@ -4570,10 +4595,6 @@ jint os::init_2(void) {
return JNI_ERR;
}

if (AdjustStackSizeForTLS) {
get_minstack_init();
}

// Check and sets minimum stack sizes against command line options
if (Posix::set_minimum_stack_sizes() == JNI_ERR) {
return JNI_ERR;
Expand All @@ -4596,6 +4617,11 @@ jint os::init_2(void) {
log_info(os)("HotSpot is running with %s, %s",
Linux::libc_version(), Linux::libpthread_version());

#ifdef __GLIBC__
// Check if we need to adjust the stack size for glibc guard pages.
init_adjust_stacksize_for_guard_pages();
#endif

if (UseNUMA || UseNUMAInterleaving) {
Linux::numa_init();
}
Expand Down Expand Up @@ -5370,9 +5396,9 @@ bool os::start_debugging(char *buf, int buflen) {
//
// ** P1 (aka bottom) and size (P2 = P1 - size) are the address and stack size
// returned from pthread_attr_getstack().
// ** Due to NPTL implementation error, linux takes the glibc guard page out
// of the stack size given in pthread_attr. We work around this for
// threads created by the VM. (We adapt bottom to be P1 and size accordingly.)
// ** If adjustStackSizeForGuardPages() is true the guard pages have been taken
// out of the stack size given in pthread_attr. We work around this for
// threads created by the VM. We adjust bottom to be P1 and size accordingly.
//
#ifndef ZERO
static void current_stack_region(address * bottom, size_t * size) {
Expand All @@ -5399,14 +5425,15 @@ static void current_stack_region(address * bottom, size_t * size) {
fatal("Cannot locate current stack attributes!");
}

// Work around NPTL stack guard error.
size_t guard_size = 0;
rslt = pthread_attr_getguardsize(&attr, &guard_size);
if (rslt != 0) {
fatal("pthread_attr_getguardsize failed with error = %d", rslt);
if (os::Linux::adjustStackSizeForGuardPages()) {
size_t guard_size = 0;
rslt = pthread_attr_getguardsize(&attr, &guard_size);
if (rslt != 0) {
fatal("pthread_attr_getguardsize failed with error = %d", rslt);
}
*bottom += guard_size;
*size -= guard_size;
}
*bottom += guard_size;
*size -= guard_size;

pthread_attr_destroy(&attr);

Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/os/linux/os_linux.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ class Linux {
// Return default guard size for the specified thread type
static size_t default_guard_size(os::ThreadType thr_type);

static bool adjustStackSizeForGuardPages(); // See comments in os_linux.cpp

static void capture_initial_stack(size_t max_size);

// Stack overflow handling
Expand Down
4 changes: 2 additions & 2 deletions src/hotspot/os/posix/os_posix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -881,8 +881,8 @@ char* os::Posix::describe_pthread_attr(char* buf, size_t buflen, const pthread_a
int detachstate = 0;
pthread_attr_getstacksize(attr, &stack_size);
pthread_attr_getguardsize(attr, &guard_size);
// Work around linux NPTL implementation error, see also os::create_thread() in os_linux.cpp.
LINUX_ONLY(stack_size -= guard_size);
// Work around glibc stack guard issue, see os::create_thread() in os_linux.cpp.
LINUX_ONLY(if (os::Linux::adjustStackSizeForGuardPages()) stack_size -= guard_size;)
pthread_attr_getdetachstate(attr, &detachstate);
jio_snprintf(buf, buflen, "stacksize: " SIZE_FORMAT "k, guardsize: " SIZE_FORMAT "k, %s",
stack_size / 1024, guard_size / 1024,
Expand Down

0 comments on commit 832707b

Please sign in to comment.