diff options
30 files changed, 248 insertions, 268 deletions
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index b1339916f0..4ba3c4b9bb 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -354,13 +354,14 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { * We can safely skip the stack overflow check if we're * a leaf *and* our frame size < fudge factor. */ - bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kArm); + bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kArm); NewLIR0(kPseudoMethodEntry); - const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm) - - Thread::kStackOverflowSignalReservedBytes; + const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm); bool large_frame = (static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes); + bool generate_explicit_stack_overflow_check = large_frame || + !cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks(); if (!skip_overflow_check) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + if (generate_explicit_stack_overflow_check) { if (!large_frame) { /* Load stack limit */ LockTemp(rs_r12); @@ -399,7 +400,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { const int spill_size = spill_count * 4; const int frame_size_without_spills = frame_size_ - spill_size; if (!skip_overflow_check) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + if (generate_explicit_stack_overflow_check) { class StackOverflowSlowPath : public LIRSlowPath { public: StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, bool restore_lr, size_t sp_displace) diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc index 28b747b591..0538c31fb8 100644 --- a/compiler/dex/quick/arm64/call_arm64.cc +++ b/compiler/dex/quick/arm64/call_arm64.cc @@ -329,16 +329,20 @@ void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) * We can safely skip the stack overflow check if we're * a leaf *and* our frame size < fudge factor. */ - bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kArm64); + bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kArm64); NewLIR0(kPseudoMethodEntry); + const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm64); + const bool large_frame = static_cast<size_t>(frame_size_) > kStackOverflowReservedUsableBytes; + bool generate_explicit_stack_overflow_check = large_frame || + !cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks(); const int spill_count = num_core_spills_ + num_fp_spills_; const int spill_size = (spill_count * kArm64PointerSize + 15) & ~0xf; // SP 16 byte alignment. const int frame_size_without_spills = frame_size_ - spill_size; if (!skip_overflow_check) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + if (generate_explicit_stack_overflow_check) { // Load stack limit LoadWordDisp(rs_xSELF, Thread::StackEndOffset<8>().Int32Value(), rs_xIP1); } else { @@ -365,7 +369,7 @@ void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) } if (!skip_overflow_check) { - if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { + if (generate_explicit_stack_overflow_check) { class StackOverflowSlowPath: public LIRSlowPath { public: StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace) : diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index 4577a4c904..e8cb356186 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -303,7 +303,7 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) * We can safely skip the stack overflow check if we're * a leaf *and* our frame size < fudge factor. */ - bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kMips); + bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kMips); NewLIR0(kPseudoMethodEntry); RegStorage check_reg = AllocTemp(); RegStorage new_sp = AllocTemp(); diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index f5f86717b4..996689a46c 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -219,7 +219,7 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { * a leaf *and* our frame size < fudge factor. */ InstructionSet isa = cu_->target64 ? kX86_64 : kX86; - const bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, isa); + bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, isa); // If we doing an implicit stack overflow check, perform the load immediately // before the stack pointer is decremented and anything is saved. diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index eccc970042..2c954a0502 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -265,7 +265,7 @@ InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGene codegen_(codegen) {} void CodeGeneratorARM::GenerateFrameEntry() { - bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kArm); + bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm); if (!skip_overflow_check) { if (kExplicitStackOverflowCheck) { SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM(); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index ab53b17636..35b811628b 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -241,7 +241,7 @@ void CodeGeneratorX86::GenerateFrameEntry() { static const int kFakeReturnRegister = 8; core_spill_mask_ |= (1 << kFakeReturnRegister); - bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86); + bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86); if (!skip_overflow_check && !kExplicitStackOverflowCheck) { __ testl(EAX, Address(ESP, -static_cast<int32_t>(GetStackOverflowReservedBytes(kX86)))); RecordPcInfo(0); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index e4259f51b4..c4571caf27 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -209,7 +209,7 @@ void CodeGeneratorX86_64::GenerateFrameEntry() { core_spill_mask_ |= (1 << kFakeReturnRegister); bool skip_overflow_check = IsLeafMethod() - && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86_64); + && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64); if (!skip_overflow_check && !kExplicitStackOverflowCheck) { __ testq(CpuRegister(RAX), Address( diff --git a/compiler/utils/stack_checks.h b/compiler/utils/stack_checks.h index 63adbc2328..ce01077808 100644 --- a/compiler/utils/stack_checks.h +++ b/compiler/utils/stack_checks.h @@ -33,10 +33,9 @@ static constexpr size_t kSmallFrameSize = 1 * KB; // Determine whether a frame is small or large, used in the decision on whether to elide a // stack overflow check on method entry. // -// A frame is considered large when it's either above kLargeFrameSize, or a quarter of the -// overflow-usable stack space. -static inline bool IsLargeFrame(size_t size, InstructionSet isa) { - return size >= kLargeFrameSize || size >= GetStackOverflowReservedBytes(isa) / 4; +// A frame is considered large when it's above kLargeFrameSize. +static inline bool FrameNeedsStackCheck(size_t size, InstructionSet isa) { + return size >= kLargeFrameSize; } } // namespace art diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h index ae30aee24d..330924ef0f 100644 --- a/runtime/arch/arm/asm_support_arm.h +++ b/runtime/arch/arm/asm_support_arm.h @@ -24,9 +24,9 @@ // Offset of field Thread::tls32_.thin_lock_thread_id verified in InitCpu #define THREAD_ID_OFFSET 12 // Offset of field Thread::tlsPtr_.card_table verified in InitCpu -#define THREAD_CARD_TABLE_OFFSET 112 +#define THREAD_CARD_TABLE_OFFSET 120 // Offset of field Thread::tlsPtr_.exception verified in InitCpu -#define THREAD_EXCEPTION_OFFSET 116 +#define THREAD_EXCEPTION_OFFSET 124 #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 176 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32 diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc index be28544af0..28b69ecf27 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -35,7 +35,7 @@ namespace art { extern "C" void art_quick_throw_null_pointer_exception(); -extern "C" void art_quick_throw_stack_overflow_from_signal(); +extern "C" void art_quick_throw_stack_overflow(); extern "C" void art_quick_implicit_suspend(); // Get the size of a thumb2 instruction in bytes. @@ -194,40 +194,19 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kArm); - Thread* self = reinterpret_cast<Thread*>(sc->arm_r9); - CHECK_EQ(self, Thread::Current()); - uintptr_t pregion = reinterpret_cast<uintptr_t>(self->GetStackEnd()) - - Thread::kStackOverflowProtectedSize; - // Check that the fault address is the value expected for a stack overflow. if (fault_addr != overflow_addr) { VLOG(signals) << "Not a stack overflow"; return false; } - // We know this is a stack overflow. We need to move the sp to the overflow region - // that exists below the protected region. Determine the address of the next - // available valid address below the protected region. - uintptr_t prevsp = sp; - sp = pregion; - VLOG(signals) << "setting sp to overflow region at " << std::hex << sp; - - // Since the compiler puts the implicit overflow - // check before the callee save instructions, the SP is already pointing to - // the previous frame. - VLOG(signals) << "previous frame: " << std::hex << prevsp; - - // Now establish the stack pointer for the signal return. - sc->arm_sp = prevsp; - - // Tell the stack overflow code where the new stack pointer should be. - sc->arm_ip = sp; // aka r12 + VLOG(signals) << "Stack overflow found"; - // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal. + // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from. // The value of LR must be the same as it was when we entered the code that // caused this fault. This will be inserted into a callee save frame by - // the function to which this handler returns (art_quick_throw_stack_overflow_from_signal). - sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow_from_signal); + // the function to which this handler returns (art_quick_throw_stack_overflow). + sc->arm_pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow); // The kernel will now return to the address in sc->arm_pc. return true; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index 6c63a1afc1..dd1f04a9b4 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -235,31 +235,6 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFr */ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFromCode - /* - * Invoke stack overflow exception from signal handler. - * On entry: - * r9: thread - * sp: address of last known frame - * r12: address of next valid SP below protected region in stack - * - * This is deceptively simple but hides some complexity. It is called in the case of - * a stack overflow condition during implicit checks. The signal handler has been - * called by the kernel due to a load from the protected stack region. The handler - * works out the address of the previous frame and passes this in SP. However there - * is a piece of memory somewhere below the current SP that is not accessible (the - * memory that caused the signal). The signal handler works out the next - * accessible value of SP and passes this in r12. This code then sets up the SP - * to be this new value and calls the code to create and throw the stack overflow - * exception. - */ -ENTRY art_quick_throw_stack_overflow_from_signal - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context - mov r0, r9 @ pass Thread::Current - mov r1, sp @ pass SP - mov sp, r12 @ move SP down to below protected region. - b artThrowStackOverflowFromCode @ artThrowStackOverflowFromCode(Thread*, SP) -END art_quick_throw_stack_overflow_from_signal - /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h index 7f0f56f274..a926449964 100644 --- a/runtime/arch/arm64/asm_support_arm64.h +++ b/runtime/arch/arm64/asm_support_arm64.h @@ -30,9 +30,9 @@ // Offset of field Thread::suspend_count_ #define THREAD_FLAGS_OFFSET 0 // Offset of field Thread::card_table_ -#define THREAD_CARD_TABLE_OFFSET 112 +#define THREAD_CARD_TABLE_OFFSET 120 // Offset of field Thread::exception_ -#define THREAD_EXCEPTION_OFFSET 120 +#define THREAD_EXCEPTION_OFFSET 128 // Offset of field Thread::thin_lock_thread_id_ #define THREAD_ID_OFFSET 12 diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc index 3a7e6896a1..b5948cbc9e 100644 --- a/runtime/arch/arm64/fault_handler_arm64.cc +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -27,7 +27,7 @@ #include "thread.h" #include "thread-inl.h" -extern "C" void art_quick_throw_stack_overflow_from_signal(); +extern "C" void art_quick_throw_stack_overflow(); extern "C" void art_quick_throw_null_pointer_exception(); extern "C" void art_quick_implicit_suspend(); @@ -157,40 +157,19 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kArm64); - Thread* self = reinterpret_cast<Thread*>(sc->regs[art::arm64::TR]); - CHECK_EQ(self, Thread::Current()); - uintptr_t pregion = reinterpret_cast<uintptr_t>(self->GetStackEnd()) - - Thread::kStackOverflowProtectedSize; - // Check that the fault address is the value expected for a stack overflow. if (fault_addr != overflow_addr) { VLOG(signals) << "Not a stack overflow"; return false; } - // We know this is a stack overflow. We need to move the sp to the overflow region - // that exists below the protected region. Determine the address of the next - // available valid address below the protected region. - uintptr_t prevsp = sp; - sp = pregion; - VLOG(signals) << "setting sp to overflow region at " << std::hex << sp; - - // Since the compiler puts the implicit overflow - // check before the callee save instructions, the SP is already pointing to - // the previous frame. - VLOG(signals) << "previous frame: " << std::hex << prevsp; - - // Now establish the stack pointer for the signal return. - sc->sp = prevsp; - - // Tell the stack overflow code where the new stack pointer should be. - sc->regs[art::arm64::IP0] = sp; // aka x16 + VLOG(signals) << "Stack overflow found"; - // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal. + // Now arrange for the signal handler to return to art_quick_throw_stack_overflow. // The value of LR must be the same as it was when we entered the code that // caused this fault. This will be inserted into a callee save frame by - // the function to which this handler returns (art_quick_throw_stack_overflow_from_signal). - sc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow_from_signal); + // the function to which this handler returns (art_quick_throw_stack_overflow). + sc->pc = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow); // The kernel will now return to the address in sc->pc. return true; diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 62ae099c1c..ab9035ae45 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -435,31 +435,6 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFr */ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFromCode - /* - * Invoke stack overflow exception from signal handler. - * On entry: - * xSELF: thread - * SP: address of last known frame - * IP0: address of next valid SP below protected region in stack - * - * This is deceptively simple but hides some complexity. It is called in the case of - * a stack overflow condition during implicit checks. The signal handler has been - * called by the kernel due to a load from the protected stack region. The handler - * works out the address of the previous frame and passes this in SP. However there - * is a piece of memory somewhere below the current SP that is not accessible (the - * memory that caused the signal). The signal handler works out the next - * accessible value of SP and passes this in x16/IP0. This code then sets up the SP - * to be this new value and calls the code to create and throw the stack overflow - * exception. - */ -ENTRY art_quick_throw_stack_overflow_from_signal - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context - mov x0, xSELF // pass Thread::Current - mov x1, sp // pass SP - mov sp, xIP0 // move SP down to below protected region. - b artThrowStackOverflowFromCode // artThrowStackOverflowFromCode(Thread*, SP) -END art_quick_throw_stack_overflow_from_signal - /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/x0 with the target Method*, arg0/x0 will contain diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h index 531ed77ef4..c9f5a25eea 100644 --- a/runtime/arch/x86/asm_support_x86.h +++ b/runtime/arch/x86/asm_support_x86.h @@ -20,11 +20,11 @@ #include "asm_support.h" // Offset of field Thread::self_ verified in InitCpu -#define THREAD_SELF_OFFSET 148 +#define THREAD_SELF_OFFSET 156 // Offset of field Thread::card_table_ verified in InitCpu -#define THREAD_CARD_TABLE_OFFSET 112 +#define THREAD_CARD_TABLE_OFFSET 120 // Offset of field Thread::exception_ verified in InitCpu -#define THREAD_EXCEPTION_OFFSET 116 +#define THREAD_EXCEPTION_OFFSET 124 // Offset of field Thread::thin_lock_thread_id_ verified in InitCpu #define THREAD_ID_OFFSET 12 diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc index 89baded816..c143c5d825 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -34,7 +34,7 @@ #define CTX_ESP uc_mcontext->__ss.__rsp #define CTX_EIP uc_mcontext->__ss.__rip #define CTX_EAX uc_mcontext->__ss.__rax -#define CTX_METHOD uc_mcontext->__ss.__rax +#define CTX_METHOD uc_mcontext->__ss.__rdi #else // 32 bit mac build. #define CTX_ESP uc_mcontext->__ss.__esp @@ -71,7 +71,7 @@ extern "C" void _art_quick_test_suspend(); #define EXT_SYM(sym) _ ## sym #else extern "C" void art_quick_throw_null_pointer_exception(); -extern "C" void art_quick_throw_stack_overflow_from_signal(); +extern "C" void art_quick_throw_stack_overflow(); extern "C" void art_quick_test_suspend(); #define EXT_SYM(sym) sym #endif @@ -382,30 +382,20 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kX86); #endif - Thread* self = Thread::Current(); - uintptr_t pregion = reinterpret_cast<uintptr_t>(self->GetStackEnd()) - - Thread::kStackOverflowProtectedSize; - // Check that the fault address is the value expected for a stack overflow. if (fault_addr != overflow_addr) { VLOG(signals) << "Not a stack overflow"; return false; } - // We know this is a stack overflow. We need to move the sp to the overflow region - // that exists below the protected region. Determine the address of the next - // available valid address below the protected region. - VLOG(signals) << "setting sp to overflow region at " << std::hex << pregion; + VLOG(signals) << "Stack overflow found"; // Since the compiler puts the implicit overflow // check before the callee save instructions, the SP is already pointing to // the previous frame. - // Tell the stack overflow code where the new stack pointer should be. - uc->CTX_EAX = pregion; - - // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal. - uc->CTX_EIP = reinterpret_cast<uintptr_t>(EXT_SYM(art_quick_throw_stack_overflow_from_signal)); + // Now arrange for the signal handler to return to art_quick_throw_stack_overflow. + uc->CTX_EIP = reinterpret_cast<uintptr_t>(art_quick_throw_stack_overflow); return true; } diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index dc4019d2a4..75ec49deb0 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -174,21 +174,6 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode */ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode -// On entry to this function, EAX contains the ESP value for the overflow region. -DEFINE_FUNCTION art_quick_throw_stack_overflow_from_signal - // Here, the ESP is above the protected region. We need to create a - // callee save frame and then move ESP down to the overflow region. - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context - mov %esp, %ecx // get current stack pointer - mov %eax, %esp // move ESP to the overflow region. - PUSH ecx // pass SP - pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() - CFI_ADJUST_CFA_OFFSET(4) - SETUP_GOT_NOSAVE // clobbers ebx (harmless here) - call PLT_SYMBOL(artThrowStackOverflowFromCode) // artThrowStackOverflowFromCode(Thread*, SP) - int3 // unreached -END_FUNCTION art_quick_throw_stack_overflow_from_signal - /* * Called by managed code, saves callee saves and then calls artThrowException * that will place a mock Method* at the bottom of the stack. Arg1 holds the exception. diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h index c3637ef7ae..40958dcdf1 100644 --- a/runtime/arch/x86_64/asm_support_x86_64.h +++ b/runtime/arch/x86_64/asm_support_x86_64.h @@ -28,11 +28,11 @@ #define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 16 // Offset of field Thread::self_ verified in InitCpu -#define THREAD_SELF_OFFSET 184 +#define THREAD_SELF_OFFSET 192 // Offset of field Thread::card_table_ verified in InitCpu -#define THREAD_CARD_TABLE_OFFSET 112 +#define THREAD_CARD_TABLE_OFFSET 120 // Offset of field Thread::exception_ verified in InitCpu -#define THREAD_EXCEPTION_OFFSET 120 +#define THREAD_EXCEPTION_OFFSET 128 // Offset of field Thread::thin_lock_thread_id_ verified in InitCpu #define THREAD_ID_OFFSET 12 diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index cbf0f38721..48bc240dab 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -284,18 +284,6 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode */ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode -// On entry to this function, RAX contains the ESP value for the overflow region. -DEFINE_FUNCTION SYMBOL(art_quick_throw_stack_overflow_from_signal) - // Here, the RSP is above the protected region. We need to create a - // callee save frame and then move RSP down to the overflow region. - SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context - mov %rsp, %rsi // get current stack pointer, pass SP as second arg - mov %rax, %rsp // move RSP to the overflow region. - mov %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current() as first arg - call PLT_SYMBOL(artThrowStackOverflowFromCode) // artThrowStackOverflowFromCode(Thread*, SP) - int3 // unreached -END_FUNCTION SYMBOL(art_quick_throw_stack_overflow_from_signal) - /* * Called by managed code, saves callee saves and then calls artThrowException * that will place a mock Method* at the bottom of the stack. Arg1 holds the exception. diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 4755b9e9db..a0e35f88fa 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -212,6 +212,11 @@ void ThrowStackOverflowError(Thread* self) { bool explicit_overflow_check = Runtime::Current()->ExplicitStackOverflowChecks(); self->ResetDefaultStackEnd(!explicit_overflow_check); // Return to default stack size. + + // And restore protection if implicit checks are on. + if (!explicit_overflow_check) { + self->ProtectStack(); + } } void CheckReferenceResult(mirror::Object* o, Thread* self) { diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index 879010e36e..13decc8068 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -58,8 +58,10 @@ extern "C" void artThrowNullPointerExceptionFromCode(Thread* self, StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + self->NoteSignalBeingHandled(); ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionFromDexPC(throw_location); + self->NoteSignalHandlerDone(); self->QuickDeliverException(); } @@ -83,7 +85,9 @@ extern "C" void artThrowArrayBoundsFromCode(int index, int length, Thread* self, extern "C" void artThrowStackOverflowFromCode(Thread* self, StackReference<mirror::ArtMethod>* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); + self->NoteSignalBeingHandled(); ThrowStackOverflowError(self); + self->NoteSignalHandlerDone(); self->QuickDeliverException(); } diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index ae1b94fa90..f572d27c39 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -70,6 +70,10 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFP(Thread, tls32_, daemon, throwing_OutOfMemoryError, 4); EXPECT_OFFSET_DIFFP(Thread, tls32_, throwing_OutOfMemoryError, no_thread_suspension, 4); EXPECT_OFFSET_DIFFP(Thread, tls32_, no_thread_suspension, thread_exit_check_count, 4); + EXPECT_OFFSET_DIFFP(Thread, tls32_, thread_exit_check_count, + is_exception_reported_to_instrumentation_, 4); + EXPECT_OFFSET_DIFFP(Thread, tls32_, is_exception_reported_to_instrumentation_, + handling_signal_, 4); // TODO: Better connection. Take alignment into account. EXPECT_OFFSET_DIFF_GT3(Thread, tls32_.thread_exit_check_count, tls64_.trace_clock_base, 4, diff --git a/runtime/instruction_set.cc b/runtime/instruction_set.cc index d7e358ce96..d8a38f42d7 100644 --- a/runtime/instruction_set.cc +++ b/runtime/instruction_set.cc @@ -87,11 +87,10 @@ size_t GetInstructionSetAlignment(InstructionSet isa) { static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB; static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes; -// TODO: Lower once implicit stack-overflow checks can work with less than 16K. -static constexpr size_t kArmStackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB; -static constexpr size_t kArm64StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB; -static constexpr size_t kX86StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB; -static constexpr size_t kX86_64StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB; +static constexpr size_t kArmStackOverflowReservedBytes = 8 * KB; +static constexpr size_t kArm64StackOverflowReservedBytes = 8 * KB; +static constexpr size_t kX86StackOverflowReservedBytes = 8 * KB; +static constexpr size_t kX86_64StackOverflowReservedBytes = 8 * KB; size_t GetStackOverflowReservedBytes(InstructionSet isa) { switch (isa) { diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 8eacb1c3d7..e168fb2f8e 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -283,6 +283,11 @@ uint32_t ArtMethod::FindCatchBlock(Handle<ArtMethod> h_this, Handle<Class> excep void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty) { + if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { + ThrowStackOverflowError(self); + return; + } + if (kIsDebugBuild) { self->AssertThreadSuspensionIsAllowable(); CHECK_EQ(kRunnable, self->GetState()); diff --git a/runtime/oat.cc b/runtime/oat.cc index 0a8c35b561..ede108cd19 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -23,7 +23,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '3', '8', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '3', '9', '\0' }; static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) { size_t estimate = 0U; diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 0169cccbf0..a2209e3eab 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -446,6 +446,14 @@ static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa, JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, va_list args) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { + ThrowStackOverflowError(soa.Self()); + return JValue(); + } + mirror::ArtMethod* method = soa.DecodeMethod(mid); mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode<mirror::Object*>(obj); uint32_t shorty_len = 0; @@ -459,6 +467,14 @@ JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject o JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::Object* receiver, jmethodID mid, jvalue* args) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { + ThrowStackOverflowError(soa.Self()); + return JValue(); + } + mirror::ArtMethod* method = soa.DecodeMethod(mid); uint32_t shorty_len = 0; const char* shorty = method->GetShorty(&shorty_len); @@ -471,6 +487,14 @@ JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::O JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::Object* receiver, jmethodID mid, jvalue* args) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { + ThrowStackOverflowError(soa.Self()); + return JValue(); + } + mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid)); uint32_t shorty_len = 0; const char* shorty = method->GetShorty(&shorty_len); @@ -483,6 +507,14 @@ JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnab JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, va_list args) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { + ThrowStackOverflowError(soa.Self()); + return JValue(); + } + mirror::Object* receiver = soa.Decode<mirror::Object*>(obj); mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid)); uint32_t shorty_len = 0; @@ -496,6 +528,14 @@ JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnab void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset, MethodHelper& mh, JValue* result) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { + ThrowStackOverflowError(self); + return; + } + ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset); shadow_frame->GetMethod()->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, @@ -504,6 +544,15 @@ void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod, jobject javaReceiver, jobject javaArgs, bool accessible) { + // We want to make sure that the stack is not within a small distance from the + // protected region in case we are calling into a leaf function whose stack + // check has been elided. + if (UNLIKELY(__builtin_frame_address(0) < + soa.Self()->GetStackEndForInterpreter(true))) { + ThrowStackOverflowError(soa.Self()); + return nullptr; + } + mirror::ArtMethod* m = mirror::ArtMethod::FromReflectedMethod(soa, javaMethod); mirror::Class* declaring_class = m->GetDeclaringClass(); diff --git a/runtime/thread.cc b/runtime/thread.cc index 8e6da74e5e..7aabfceb2b 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -76,8 +76,7 @@ namespace art { bool Thread::is_started_ = false; pthread_key_t Thread::pthread_key_self_; ConditionVariable* Thread::resume_cond_ = nullptr; -const size_t Thread::kStackOverflowImplicitCheckSize = kStackOverflowProtectedSize + - GetStackOverflowReservedBytes(kRuntimeISA); +const size_t Thread::kStackOverflowImplicitCheckSize = GetStackOverflowReservedBytes(kRuntimeISA); static const char* kThreadNameDuringStartup = "<native thread without managed peer>"; @@ -238,92 +237,48 @@ static size_t FixStackSize(size_t stack_size) { byte dont_optimize_this; // Install a protected region in the stack. This is used to trigger a SIGSEGV if a stack -// overflow is detected. It is located right below the stack_end_. Just below that -// is the StackOverflow reserved region used when creating the StackOverflow -// exception. +// overflow is detected. It is located right below the stack_begin_. // -// There is a little complexity here that deserves a special mention. When running on the -// host (glibc), the process's main thread's stack is allocated with a special flag +// There is a little complexity here that deserves a special mention. On some +// architectures, the stack created using a VM_GROWSDOWN flag // to prevent memory being allocated when it's not needed. This flag makes the // kernel only allocate memory for the stack by growing down in memory. Because we // want to put an mprotected region far away from that at the stack top, we need // to make sure the pages for the stack are mapped in before we call mprotect. We do // this by reading every page from the stack bottom (highest address) to the stack top. // We then madvise this away. -void Thread::InstallImplicitProtection(bool is_main_stack) { - byte* pregion = tlsPtr_.stack_end; - byte* stack_lowmem = tlsPtr_.stack_begin; - byte* stack_top = reinterpret_cast<byte*>(reinterpret_cast<uintptr_t>(&pregion) & +void Thread::InstallImplicitProtection() { + byte* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize; + byte* stack_himem = tlsPtr_.stack_end; + byte* stack_top = reinterpret_cast<byte*>(reinterpret_cast<uintptr_t>(&stack_himem) & ~(kPageSize - 1)); // Page containing current top of stack. - const bool running_on_intel = (kRuntimeISA == kX86) || (kRuntimeISA == kX86_64); - - if (running_on_intel) { - // On Intel, we need to map in the main stack. This must be done by reading from the - // current stack pointer downwards as the stack is mapped using VM_GROWSDOWN - // in the kernel. Any access more than a page below the current SP will cause - // a segv. - if (is_main_stack) { - // First we need to unprotect the protected region because this may - // be called more than once for a particular stack and we will crash - // if we try to read the protected page. - mprotect(pregion - kStackOverflowProtectedSize, kStackOverflowProtectedSize, PROT_READ); - - // Read every page from the high address to the low. - for (byte* p = stack_top; p > stack_lowmem; p -= kPageSize) { - dont_optimize_this = *p; - } - } - } + // First remove the protection on the protected region as will want to read and + // write it. This may fail (on the first attempt when the stack is not mapped) + // but we ignore that. + UnprotectStack(); - // Check and place a marker word at the lowest usable address in the stack. This - // is used to prevent a double protection. - constexpr uint32_t kMarker = 0xdadadada; - uintptr_t *marker = reinterpret_cast<uintptr_t*>(pregion); - if (*marker == kMarker) { - // The region has already been set up. But on the main stack on the host we have - // removed the protected region in order to read the stack memory. We need to put - // this back again. - if (is_main_stack && running_on_intel) { - mprotect(pregion - kStackOverflowProtectedSize, kStackOverflowProtectedSize, PROT_NONE); - madvise(stack_lowmem, stack_top - stack_lowmem, MADV_DONTNEED); - } - return; - } - // Add marker so that we can detect a second attempt to do this. - *marker = kMarker; + // Map in the stack. This must be done by reading from the + // current stack pointer downwards as the stack may be mapped using VM_GROWSDOWN + // in the kernel. Any access more than a page below the current SP might cause + // a segv. - if (!running_on_intel) { - // Running on !Intel, stacks are mapped cleanly. The protected region for the - // main stack just needs to be mapped in. We do this by writing one byte per page. - for (byte* p = pregion - kStackOverflowProtectedSize; p < pregion; p += kPageSize) { - *p = 0; - } + // Read every page from the high address to the low. + for (byte* p = stack_top; p > pregion; p -= kPageSize) { + dont_optimize_this = *p; } - pregion -= kStackOverflowProtectedSize; - VLOG(threads) << "installing stack protected region at " << std::hex << static_cast<void*>(pregion) << " to " << static_cast<void*>(pregion + kStackOverflowProtectedSize - 1); - - if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) { - LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. Reason:" - << strerror(errno) << kStackOverflowProtectedSize; - } + // Protect the bottom of the stack to prevent read/write to it. + ProtectStack(); // Tell the kernel that we won't be needing these pages any more. // NB. madvise will probably write zeroes into the memory (on linux it does). - if (is_main_stack) { - if (running_on_intel) { - // On the host, it's the whole stack (minus a page to prevent overwrite of stack top). - madvise(stack_lowmem, stack_top - stack_lowmem - kPageSize, MADV_DONTNEED); - } else { - // On Android, just the protected region. - madvise(pregion, kStackOverflowProtectedSize, MADV_DONTNEED); - } - } + uint32_t unwanted_size = stack_top - pregion - kPageSize; + madvise(pregion, unwanted_size, MADV_DONTNEED); } void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) { @@ -538,7 +493,13 @@ void Thread::InitStackHwm() { tlsPtr_.stack_begin = reinterpret_cast<byte*>(read_stack_base); tlsPtr_.stack_size = read_stack_size; - if (read_stack_size <= GetStackOverflowReservedBytes(kRuntimeISA)) { + // The minimum stack size we can cope with is the overflow reserved bytes (typically + // 8K) + the protected region size (4K) + another page (4K). Typically this will + // be 8+4+4 = 16K. The thread won't be able to do much with this stack even the GC takes + // between 8K and 12K. + uint32_t min_stack = GetStackOverflowReservedBytes(kRuntimeISA) + kStackOverflowProtectedSize + + 4 * KB; + if (read_stack_size <= min_stack) { LOG(FATAL) << "Attempt to attach a thread with a too-small stack (" << read_stack_size << " bytes)"; } @@ -582,20 +543,19 @@ void Thread::InitStackHwm() { // Install the protected region if we are doing implicit overflow checks. if (implicit_stack_check) { - if (is_main_thread) { - size_t guardsize; - pthread_attr_t attributes; - CHECK_PTHREAD_CALL(pthread_attr_init, (&attributes), "guard size query"); - CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, &guardsize), "guard size query"); - CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), "guard size query"); - // The main thread might have protected region at the bottom. We need - // to install our own region so we need to move the limits - // of the stack to make room for it. - tlsPtr_.stack_begin += guardsize; - tlsPtr_.stack_end += guardsize; - tlsPtr_.stack_size -= guardsize; - } - InstallImplicitProtection(is_main_thread); + size_t guardsize; + pthread_attr_t attributes; + CHECK_PTHREAD_CALL(pthread_attr_init, (&attributes), "guard size query"); + CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, &guardsize), "guard size query"); + CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), "guard size query"); + // The thread might have protected region at the bottom. We need + // to install our own region so we need to move the limits + // of the stack to make room for it. + tlsPtr_.stack_begin += guardsize; + tlsPtr_.stack_end += guardsize; + tlsPtr_.stack_size -= guardsize; + + InstallImplicitProtection(); } // Sanity check. @@ -2266,6 +2226,14 @@ void Thread::SetStackEndForStackOverflow() { } tlsPtr_.stack_end = tlsPtr_.stack_begin; + + // Remove the stack overflow protection if is it set up. + bool implicit_stack_check = !Runtime::Current()->ExplicitStackOverflowChecks(); + if (implicit_stack_check) { + if (!UnprotectStack()) { + LOG(ERROR) << "Unable to remove stack protection for stack overflow"; + } + } } void Thread::SetTlab(byte* start, byte* end) { @@ -2291,4 +2259,21 @@ std::ostream& operator<<(std::ostream& os, const Thread& thread) { return os; } +void Thread::ProtectStack() { + void* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize; + VLOG(threads) << "Protecting stack at " << pregion; + if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) { + LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. " + "Reason: " + << strerror(errno) << " size: " << kStackOverflowProtectedSize; + } +} + +bool Thread::UnprotectStack() { + void* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize; + VLOG(threads) << "Unprotecting stack at " << pregion; + return mprotect(pregion, kStackOverflowProtectedSize, PROT_READ|PROT_WRITE) == 0; +} + + } // namespace art diff --git a/runtime/thread.h b/runtime/thread.h index c2b200bf1a..120ff6fbb4 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -94,16 +94,41 @@ enum ThreadFlag { static constexpr size_t kNumRosAllocThreadLocalSizeBrackets = 34; +// Thread's stack layout for implicit stack overflow checks: +// +// +---------------------+ <- highest address of stack memory +// | | +// . . <- SP +// | | +// | | +// +---------------------+ <- stack_end +// | | +// | Gap | +// | | +// +---------------------+ <- stack_begin +// | | +// | Protected region | +// | | +// +---------------------+ <- lowest address of stack memory +// +// The stack always grows down in memory. At the lowest address is a region of memory +// that is set mprotect(PROT_NONE). Any attempt to read/write to this region will +// result in a segmentation fault signal. At any point, the thread's SP will be somewhere +// between the stack_end and the highest address in stack memory. An implicit stack +// overflow check is a read of memory at a certain offset below the current SP (4K typically). +// If the thread's SP is below the stack_end address this will be a read into the protected +// region. If the SP is above the stack_end address, the thread is guaranteed to have +// at least 4K of space. Because stack overflow checks are only performed in generated code, +// if the thread makes a call out to a native function (through JNI), that native function +// might only have 4K of memory (if the SP is adjacent to stack_end). + class Thread { public: - // How much of the reserved bytes is reserved for incoming signals. - static constexpr size_t kStackOverflowSignalReservedBytes = 2 * KB; - // For implicit overflow checks we reserve an extra piece of memory at the bottom // of the stack (lowest memory). The higher portion of the memory // is protected against reads and the lower is available for use while // throwing the StackOverflow exception. - static constexpr size_t kStackOverflowProtectedSize = 16 * KB; + static constexpr size_t kStackOverflowProtectedSize = 4 * KB; static const size_t kStackOverflowImplicitCheckSize; // Creates a new native thread corresponding to the given managed peer. @@ -582,7 +607,7 @@ class Thread { } // Install the protected region for implicit stack checks. - void InstallImplicitProtection(bool is_main_stack); + void InstallImplicitProtection(); bool IsHandlingStackOverflow() const { return tlsPtr_.stack_end == tlsPtr_.stack_begin; @@ -814,6 +839,20 @@ class Thread { tls32_.is_exception_reported_to_instrumentation_ = reported; } + void ProtectStack(); + bool UnprotectStack(); + + void NoteSignalBeingHandled() { + if (tls32_.handling_signal_) { + LOG(FATAL) << "Detected signal while processing a signal"; + } + tls32_.handling_signal_ = true; + } + + void NoteSignalHandlerDone() { + tls32_.handling_signal_ = false; + } + private: explicit Thread(bool daemon); ~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_, @@ -918,7 +957,8 @@ class Thread { explicit tls_32bit_sized_values(bool is_daemon) : suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0), daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0), - thread_exit_check_count(0), is_exception_reported_to_instrumentation_(false) { + thread_exit_check_count(0), is_exception_reported_to_instrumentation_(false), + handling_signal_(false), padding_(0) { } union StateAndFlags state_and_flags; @@ -958,6 +998,12 @@ class Thread { // When true this field indicates that the exception associated with this thread has already // been reported to instrumentation. bool32_t is_exception_reported_to_instrumentation_; + + // True if signal is being handled by this thread. + bool32_t handling_signal_; + + // Padding to make the size aligned to 8. Remove this if we add another 32 bit field. + int32_t padding_; } tls32_; struct PACKED(8) tls_64bit_sized_values { diff --git a/test/004-InterfaceTest/src/Main.java b/test/004-InterfaceTest/src/Main.java index 9ebac59445..297cbb04f1 100644 --- a/test/004-InterfaceTest/src/Main.java +++ b/test/004-InterfaceTest/src/Main.java @@ -23,7 +23,7 @@ public class Main { Integer intobj = new Integer(0); String s = "asdf"; long start = System.currentTimeMillis(); - for (int i = 0; i < 1000000; i++) { + for (int i = 0; i < 10000; i++) { map.put(intobj, s); } long end = System.currentTimeMillis(); @@ -34,7 +34,7 @@ public class Main { Integer intobj = new Integer(0); String s = "asdf"; long start = System.currentTimeMillis(); - for (int i = 0; i < 1000000; i++) { + for (int i = 0; i < 10000; i++) { map.put(intobj, s); } long end = System.currentTimeMillis(); diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc index a2dd66459c..c05dc22b8b 100644 --- a/test/004-SignalTest/signaltest.cc +++ b/test/004-SignalTest/signaltest.cc @@ -16,6 +16,7 @@ #include <signal.h> #include <stdio.h> +#include <stdlib.h> #include <unistd.h> #include "jni.h" @@ -24,8 +25,15 @@ #include <sys/ucontext.h> #endif +static int signal_count; +static const int kMaxSignal = 2; + static void signalhandler(int sig, siginfo_t* info, void* context) { printf("signal caught\n"); + ++signal_count; + if (signal_count > kMaxSignal) { + abort(); + } #ifdef __arm__ // On ARM we do a more exhaustive test to make sure the signal // context is OK. |