Skip to content
Permalink
Browse files
8254158: Consolidate per-platform stack overflow handling code
Reviewed-by: fparain, hseigel
  • Loading branch information
coleenp committed Oct 13, 2020
1 parent 715e24a commit ba5dc67a74c35e64b934ec8105a853770b30ed52
@@ -139,8 +139,6 @@ class Linux {
static intptr_t* ucontext_get_sp(const ucontext_t* uc);
static intptr_t* ucontext_get_fp(const ucontext_t* uc);

static bool get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr);

// GNU libc and libpthread version strings
static const char *libc_version() { return _libc_version; }
static const char *libpthread_version() { return _libpthread_version; }
@@ -30,6 +30,7 @@
#include "utilities/globalDefinitions.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "services/memTracker.hpp"
#include "runtime/atomic.hpp"
#include "runtime/java.hpp"
@@ -907,6 +908,119 @@ size_t os::Posix::get_initial_stack_size(ThreadType thr_type, size_t req_stack_s
return stack_size;
}

#ifndef ZERO
#ifndef ARM
static bool get_frame_at_stack_banging_point(JavaThread* thread, address pc, const void* ucVoid, frame* fr) {
if (Interpreter::contains(pc)) {
// interpreter performs stack banging after the fixed frame header has
// been generated while the compilers perform it before. To maintain
// semantic consistency between interpreted and compiled frames, the
// method returns the Java sender of the current frame.
*fr = os::fetch_frame_from_context(ucVoid);
if (!fr->is_first_java_frame()) {
// get_frame_at_stack_banging_point() is only called when we
// have well defined stacks so java_sender() calls do not need
// to assert safe_for_sender() first.
*fr = fr->java_sender();
}
} else {
// more complex code with compiled code
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
CodeBlob* cb = CodeCache::find_blob(pc);
if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) {
// Not sure where the pc points to, fallback to default
// stack overflow handling
return false;
} else {
// in compiled code, the stack banging is performed just after the return pc
// has been pushed on the stack
*fr = os::fetch_compiled_frame_from_context(ucVoid);
if (!fr->is_java_frame()) {
assert(!fr->is_first_frame(), "Safety check");
// See java_sender() comment above.
*fr = fr->java_sender();
}
}
}
assert(fr->is_java_frame(), "Safety check");
return true;
}
#endif // ARM

// This return true if the signal handler should just continue, ie. return after calling this
bool os::Posix::handle_stack_overflow(JavaThread* thread, address addr, address pc,
const void* ucVoid, address* stub) {
// stack overflow
StackOverflow* overflow_state = thread->stack_overflow_state();
if (overflow_state->in_stack_yellow_reserved_zone(addr)) {
if (thread->thread_state() == _thread_in_Java) {
#ifndef ARM
// arm32 doesn't have this
if (overflow_state->in_stack_reserved_zone(addr)) {
frame fr;
if (get_frame_at_stack_banging_point(thread, pc, ucVoid, &fr)) {
assert(fr.is_java_frame(), "Must be a Java frame");
frame activation =
SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
if (activation.sp() != NULL) {
overflow_state->disable_stack_reserved_zone();
if (activation.is_interpreted_frame()) {
overflow_state->set_reserved_stack_activation((address)(
activation.fp() + frame::interpreter_frame_initial_sp_offset));
} else {
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
}
return true; // just continue
}
}
}
#endif // ARM
// Throw a stack overflow exception. Guard pages will be reenabled
// while unwinding the stack.
overflow_state->disable_stack_yellow_reserved_zone();
*stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
} else {
// Thread was in the vm or native code. Return and try to finish.
overflow_state->disable_stack_yellow_reserved_zone();
return true; // just continue
}
} else if (overflow_state->in_stack_red_zone(addr)) {
// Fatal red zone violation. Disable the guard pages and fall through
// to handle_unexpected_exception way down below.
overflow_state->disable_stack_red_zone();
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");

// This is a likely cause, but hard to verify. Let's just print
// it as a hint.
tty->print_raw_cr("Please check if any of your loaded .so files has "
"enabled executable stack (see man page execstack(8))");

} else {
#if !defined(AIX) && !defined(__APPLE__)
// bsd and aix don't have this

// Accessing stack address below sp may cause SEGV if current
// thread has MAP_GROWSDOWN stack. This should only happen when
// current thread was created by user code with MAP_GROWSDOWN flag
// and then attached to VM. See notes in os_linux.cpp.
if (thread->osthread()->expanding_stack() == 0) {
thread->osthread()->set_expanding_stack();
if (os::Linux::manually_expand_stack(thread, addr)) {
thread->osthread()->clear_expanding_stack();
return true; // just continue
}
thread->osthread()->clear_expanding_stack();
} else {
fatal("recursive segv. expanding stack.");
}
#else
tty->print_raw_cr("SIGSEGV happened inside stack but outside yellow and red zone.");
#endif // AIX or BSD
}
return false;
}
#endif // ZERO

bool os::Posix::is_root(uid_t uid){
return ROOT_UID == uid;
}
@@ -106,6 +106,10 @@ class Posix {
#endif

static void to_RTC_abstime(timespec* abstime, int64_t millis);

static bool handle_stack_overflow(JavaThread* thread, address addr, address pc,
const void* ucVoid,
address* stub);
};

/*
@@ -142,40 +142,11 @@ frame os::fetch_frame_from_context(const void* ucVoid) {
return fr;
}

bool os::Aix::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) {
address pc = (address) os::Aix::ucontext_get_pc(uc);
if (Interpreter::contains(pc)) {
// Interpreter performs stack banging after the fixed frame header has
// been generated while the compilers perform it before. To maintain
// semantic consistency between interpreted and compiled frames, the
// method returns the Java sender of the current frame.
*fr = os::fetch_frame_from_context(uc);
if (!fr->is_first_java_frame()) {
assert(fr->safe_for_sender(thread), "Safety check");
*fr = fr->java_sender();
}
} else {
// More complex code with compiled code.
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
CodeBlob* cb = CodeCache::find_blob(pc);
if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) {
// Not sure where the pc points to, fallback to default
// stack overflow handling. In compiled code, we bang before
// the frame is complete.
return false;
} else {
intptr_t* sp = os::Aix::ucontext_get_sp(uc);
address lr = ucontext_get_lr(uc);
*fr = frame(sp, lr);
if (!fr->is_java_frame()) {
assert(fr->safe_for_sender(thread), "Safety check");
assert(!fr->is_first_frame(), "Safety check");
*fr = fr->java_sender();
}
}
}
assert(fr->is_java_frame(), "Safety check");
return true;
frame os::fetch_compiled_frame_from_context(const void* ucVoid) {
const ucontext_t* uc = (const ucontext_t*)ucVoid;
intptr_t* sp = os::Aix::ucontext_get_sp(uc);
address lr = ucontext_get_lr(uc);
*fr = frame(sp, lr);
}

frame os::get_sender_for_C_frame(frame* fr) {
@@ -267,56 +238,13 @@ JVM_handle_aix_signal(int sig, siginfo_t* info, void* ucVoid, int abort_if_unrec
// Handle ALL stack overflow variations here
if (sig == SIGSEGV && thread->is_in_full_stack(addr)) {
// stack overflow
StackOverflow* overflow_state = thread->stack_overflow_state();

//
// If we are in a yellow zone and we are inside java, we disable the yellow zone and
// throw a stack overflow exception.
// If we are in native code or VM C code, we report-and-die. The original coding tried
// to continue with yellow zone disabled, but that doesn't buy us much and prevents
// hs_err_pid files.
if (overflow_state->in_stack_yellow_reserved_zone(addr)) {
if (thread->thread_state() == _thread_in_Java) {
if (overflow_state->in_stack_reserved_zone(addr)) {
frame fr;
if (os::Aix::get_frame_at_stack_banging_point(thread, uc, &fr)) {
assert(fr.is_java_frame(), "Must be a Javac frame");
frame activation =
SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
if (activation.sp() != NULL) {
overflow_state->disable_stack_reserved_zone();
if (activation.is_interpreted_frame()) {
overflow_state->set_reserved_stack_activation((address)activation.fp());
} else {
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
}
return 1;
}
}
}
// Throw a stack overflow exception.
// Guard pages will be reenabled while unwinding the stack.
overflow_state->disable_stack_yellow_reserved_zone();
stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
goto run_stub;
} else {
// Thread was in the vm or native code. Return and try to finish.
overflow_state->disable_stack_yellow_reserved_zone();
return 1;
}
} else if (overflow_state->in_stack_red_zone(addr)) {
// Fatal red zone violation. Disable the guard pages and fall through
// to handle_unexpected_exception way down below.
overflow_state->disable_stack_red_zone();
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
goto report_and_die;
if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) {
return 1; // continue
} else if (stub != NULL) {
goto run_stub;
} else {
// This means a segv happened inside our stack, but not in
// the guarded zone. I'd like to know when this happens,
tty->print_raw_cr("SIGSEGV happened inside stack but outside yellow and red zone.");
goto report_and_die;
}

} // end handle SIGSEGV inside stack boundaries

if (thread->thread_state() == _thread_in_Java) {
@@ -339,41 +339,12 @@ frame os::fetch_frame_from_context(const void* ucVoid) {
return frame(sp, fp, epc);
}

bool os::Bsd::get_frame_at_stack_banging_point(JavaThread* thread, ucontext_t* uc, frame* fr) {
address pc = (address) os::Bsd::ucontext_get_pc(uc);
if (Interpreter::contains(pc)) {
// interpreter performs stack banging after the fixed frame header has
// been generated while the compilers perform it before. To maintain
// semantic consistency between interpreted and compiled frames, the
// method returns the Java sender of the current frame.
*fr = os::fetch_frame_from_context(uc);
if (!fr->is_first_java_frame()) {
// get_frame_at_stack_banging_point() is only called when we
// have well defined stacks so java_sender() calls do not need
// to assert safe_for_sender() first.
*fr = fr->java_sender();
}
} else {
// more complex code with compiled code
assert(!Interpreter::contains(pc), "Interpreted methods should have been handled above");
CodeBlob* cb = CodeCache::find_blob(pc);
if (cb == NULL || !cb->is_nmethod() || cb->is_frame_complete_at(pc)) {
// Not sure where the pc points to, fallback to default
// stack overflow handling
return false;
} else {
*fr = os::fetch_frame_from_context(uc);
// in compiled code, the stack banging is performed just after the return pc
// has been pushed on the stack
*fr = frame(fr->sp() + 1, fr->fp(), (address)*(fr->sp()));
if (!fr->is_java_frame()) {
// See java_sender() comment above.
*fr = fr->java_sender();
}
}
}
assert(fr->is_java_frame(), "Safety check");
return true;
frame os::fetch_compiled_frame_from_context(const void* ucVoid) {
const ucontext_t* uc = (const ucontext_t*)ucVoid;
frame fr = os::fetch_frame_from_context(uc);
// in compiled code, the stack banging is performed just after the return pc
// has been pushed on the stack
return frame(fr.sp() + 1, fr.fp(), (address)*(fr.sp()));
}

// By default, gcc always save frame pointer (%ebp/%rbp) on stack. It may get
@@ -495,40 +466,8 @@ JVM_handle_bsd_signal(int sig,
// check if fault address is within thread stack
if (thread->is_in_full_stack(addr)) {
// stack overflow
StackOverflow* overflow_state = thread->stack_overflow_state();
if (overflow_state->in_stack_yellow_reserved_zone(addr)) {
if (thread->thread_state() == _thread_in_Java) {
if (overflow_state->in_stack_reserved_zone(addr)) {
frame fr;
if (os::Bsd::get_frame_at_stack_banging_point(thread, uc, &fr)) {
assert(fr.is_java_frame(), "Must be a Java frame");
frame activation = SharedRuntime::look_for_reserved_stack_annotated_method(thread, fr);
if (activation.sp() != NULL) {
overflow_state->disable_stack_reserved_zone();
if (activation.is_interpreted_frame()) {
overflow_state->set_reserved_stack_activation((address)(
activation.fp() + frame::interpreter_frame_initial_sp_offset));
} else {
overflow_state->set_reserved_stack_activation((address)activation.unextended_sp());
}
return 1;
}
}
}
// Throw a stack overflow exception. Guard pages will be reenabled
// while unwinding the stack.
overflow_state->disable_stack_yellow_reserved_zone();
stub = SharedRuntime::continuation_for_implicit_exception(thread, pc, SharedRuntime::STACK_OVERFLOW);
} else {
// Thread was in the vm or native code. Return and try to finish.
overflow_state->disable_stack_yellow_reserved_zone();
return 1;
}
} else if (overflow_state->in_stack_red_zone(addr)) {
// Fatal red zone violation. Disable the guard pages and fall through
// to handle_unexpected_exception way down below.
overflow_state->disable_stack_red_zone();
tty->print_raw_cr("An irrecoverable stack overflow has occurred.");
if (os::Posix::handle_stack_overflow(thread, addr, pc, uc, &stub)) {
return 1; // continue
}
}
}
Loading

1 comment on commit ba5dc67

@bridgekeeper

This comment has been minimized.

Copy link

@bridgekeeper bridgekeeper bot commented on ba5dc67 Oct 13, 2020

Please sign in to comment.