ART: Scope localrefs for stackoverflow handling
Ensure that the destructor runs before the thread's stack is
protected, so as to have enough space to run the destructor
in.
Bug: 116586190
Test: SANITIZE_HOST=address art/test/testrunner/testrunner.py -b --host -t 004-SignalTest
Change-Id: I1b961e2a95a2cf91a6c25e9bf1c504e1a957d348
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 657a78b..1fdc8d7 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -771,13 +771,19 @@
// Avoid running Java code for exception initialization.
// TODO: Checks to make this a bit less brittle.
+ //
+ // Note: this lambda ensures that the destruction of the ScopedLocalRefs will run in the extended
+ // stack, which is important for modes with larger stack sizes (e.g., ASAN). Using a lambda
+ // instead of a block simplifies the control flow.
+ auto create_and_throw = [&](std::string* error_msg) REQUIRES_SHARED(Locks::mutator_lock_) {
+ // Allocate an uninitialized object.
+ ScopedLocalRef<jobject> exc(env,
+ env->AllocObject(WellKnownClasses::java_lang_StackOverflowError));
+ if (exc == nullptr) {
+ *error_msg = "Could not allocate StackOverflowError object.";
+ return false;
+ }
- std::string error_msg;
-
- // Allocate an uninitialized object.
- ScopedLocalRef<jobject> exc(env,
- env->AllocObject(WellKnownClasses::java_lang_StackOverflowError));
- if (exc.get() != nullptr) {
// "Initialize".
// StackOverflowError -> VirtualMachineError -> Error -> Throwable -> Object.
// Only Throwable has "custom" fields:
@@ -793,54 +799,55 @@
// detailMessage.
// TODO: Use String::FromModifiedUTF...?
ScopedLocalRef<jstring> s(env, env->NewStringUTF(msg.c_str()));
- if (s.get() != nullptr) {
- env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_detailMessage, s.get());
-
- // cause.
- env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_cause, exc.get());
-
- // suppressedExceptions.
- ScopedLocalRef<jobject> emptylist(env, env->GetStaticObjectField(
- WellKnownClasses::java_util_Collections,
- WellKnownClasses::java_util_Collections_EMPTY_LIST));
- CHECK(emptylist.get() != nullptr);
- env->SetObjectField(exc.get(),
- WellKnownClasses::java_lang_Throwable_suppressedExceptions,
- emptylist.get());
-
- // stackState is set as result of fillInStackTrace. fillInStackTrace calls
- // nativeFillInStackTrace.
- ScopedLocalRef<jobject> stack_state_val(env, nullptr);
- {
- ScopedObjectAccessUnchecked soa(env);
- stack_state_val.reset(soa.Self()->CreateInternalStackTrace<false>(soa));
- }
- if (stack_state_val.get() != nullptr) {
- env->SetObjectField(exc.get(),
- WellKnownClasses::java_lang_Throwable_stackState,
- stack_state_val.get());
-
- // stackTrace.
- ScopedLocalRef<jobject> stack_trace_elem(env, env->GetStaticObjectField(
- WellKnownClasses::libcore_util_EmptyArray,
- WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT));
- env->SetObjectField(exc.get(),
- WellKnownClasses::java_lang_Throwable_stackTrace,
- stack_trace_elem.get());
- } else {
- error_msg = "Could not create stack trace.";
- }
- // Throw the exception.
- self->SetException(self->DecodeJObject(exc.get())->AsThrowable());
- } else {
- // Could not allocate a string object.
- error_msg = "Couldn't throw new StackOverflowError because JNI NewStringUTF failed.";
+ if (s == nullptr) {
+ *error_msg = "Could not throw new StackOverflowError because JNI NewStringUTF failed.";
+ return false;
}
- } else {
- error_msg = "Could not allocate StackOverflowError object.";
- }
- if (!error_msg.empty()) {
+ env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_detailMessage, s.get());
+
+ // cause.
+ env->SetObjectField(exc.get(), WellKnownClasses::java_lang_Throwable_cause, exc.get());
+
+ // suppressedExceptions.
+ ScopedLocalRef<jobject> emptylist(env, env->GetStaticObjectField(
+ WellKnownClasses::java_util_Collections,
+ WellKnownClasses::java_util_Collections_EMPTY_LIST));
+ CHECK(emptylist.get() != nullptr);
+ env->SetObjectField(exc.get(),
+ WellKnownClasses::java_lang_Throwable_suppressedExceptions,
+ emptylist.get());
+
+ // stackState is set as result of fillInStackTrace. fillInStackTrace calls
+ // nativeFillInStackTrace.
+ ScopedLocalRef<jobject> stack_state_val(env, nullptr);
+ {
+ ScopedObjectAccessUnchecked soa(env); // TODO: Is this necessary?
+ stack_state_val.reset(soa.Self()->CreateInternalStackTrace<false>(soa));
+ }
+ if (stack_state_val.get() == nullptr) {
+ *error_msg = "Could not create stack trace.";
+ return false;
+ }
+
+ env->SetObjectField(exc.get(),
+ WellKnownClasses::java_lang_Throwable_stackState,
+ stack_state_val.get());
+
+ // stackTrace.
+ ScopedLocalRef<jobject> stack_trace_elem(env, env->GetStaticObjectField(
+ WellKnownClasses::libcore_util_EmptyArray,
+ WellKnownClasses::libcore_util_EmptyArray_STACK_TRACE_ELEMENT));
+ env->SetObjectField(exc.get(),
+ WellKnownClasses::java_lang_Throwable_stackTrace,
+ stack_trace_elem.get());
+
+ // Throw the exception.
+ self->SetException(self->DecodeJObject(exc.get())->AsThrowable());
+ return true;
+ };
+ std::string error_msg;
+ if (!create_and_throw(&error_msg)) {
LOG(WARNING) << error_msg;
CHECK(self->IsExceptionPending());
}