blob: f85247836318be40102f52b6f98949a1fceb31bf [file] [log] [blame]
Avi Drissmane4622aa2022-09-08 20:36:061// Copyright 2012 The Chromium Authors
[email protected]61c86c62011-08-02 16:11:162// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/android/jni_android.h"
6
avib30f2402015-12-24 03:43:287#include <stddef.h>
erikchen011ad3f2018-01-26 17:54:558#include <sys/prctl.h>
avib30f2402015-12-24 03:43:289
Joshua Peraza7814da22018-07-10 21:37:5010#include "base/android/java_exception_reporter.h"
[email protected]1b46a532012-05-23 05:59:4911#include "base/android/jni_string.h"
Clark DuVallc131c502020-11-26 16:23:4912#include "base/android/jni_utils.h"
Andrew Grieved03f4ca2023-09-20 19:20:5413#include "base/android_runtime_jni_headers/Throwable_jni.h"
Scott Violet44165792018-02-22 02:08:0814#include "base/debug/debugging_buildflags.h"
Etienne Dechamps8af082f82023-11-03 14:30:5215#include "base/feature_list.h"
[email protected]61c86c62011-08-02 16:11:1616#include "base/logging.h"
Andrew Grieve4ad46dc2024-06-20 19:22:0917#include "base/strings/string_util.h"
Andrew Grieve75cd62b2022-10-18 20:39:5018#include "build/build_config.h"
Andrew Grieve2f99f2872024-02-07 17:41:5619#include "build/robolectric_buildflags.h"
Sam Maier8509cc62024-02-14 21:57:1420#include "third_party/jni_zero/jni_zero.h"
[email protected]61c86c62011-08-02 16:11:1621
Andrew Grieve70a2ba22025-02-04 15:51:0322// Must come after all headers that specialize FromJniType() / ToJniType().
Mohannad Farragf1d08922025-04-16 14:52:3623#include "base/jni_android_jni/JniAndroid_jni.h"
Kyle Farnung7d4a99952024-03-26 21:15:1624
Clark DuVallc131c502020-11-26 16:23:4925namespace base {
26namespace android {
[email protected]61c86c62011-08-02 16:11:1627namespace {
[email protected]fe0f1ab2012-02-09 21:02:2728
Etienne Dechamps8af082f82023-11-03 14:30:5229// If disabled, we LOG(FATAL) immediately in native code when faced with an
30// uncaught Java exception (historical behavior). If enabled, we give the Java
31// uncaught exception handler a chance to handle the exception first, so that
32// the crash is (hopefully) seen as a Java crash, not a native crash.
Alison Gale81f4f2c72024-04-22 19:33:3133// TODO(crbug.com/40261529): remove this switch once we are confident the
Etienne Dechamps8af082f82023-11-03 14:30:5234// new behavior is fine.
35BASE_FEATURE(kHandleExceptionsInJava,
36 "HandleJniExceptionsInJava",
37 base::FEATURE_ENABLED_BY_DEFAULT);
38
Andrew Grieve0e0938a72023-11-28 14:58:2739jclass g_out_of_memory_error_class = nullptr;
[email protected]96e7ade2011-12-05 14:42:0840
Andrew Grieve2f99f2872024-02-07 17:41:5641#if !BUILDFLAG(IS_ROBOLECTRIC)
42jmethodID g_class_loader_load_class_method_id = nullptr;
43// ClassLoader.loadClass() accepts either slashes or dots on Android, but JVM
44// requires dots. We could translate, but there is no need to go through
45// ClassLoaders in Robolectric anyways.
46// https://cs.android.com/search?q=symbol:DexFile_defineClassNative
Sam Maier06caed52024-01-25 17:10:4647jclass GetClassFromSplit(JNIEnv* env,
48 const char* class_name,
49 const char* split_name) {
Andrew Grieve4ad46dc2024-06-20 19:22:0950 DCHECK(IsStringASCII(class_name));
51 ScopedJavaLocalRef<jstring> j_class_name(env, env->NewStringUTF(class_name));
Sam Maier06caed52024-01-25 17:10:4652 return static_cast<jclass>(env->CallObjectMethod(
53 GetSplitClassLoader(env, split_name), g_class_loader_load_class_method_id,
Andrew Grieve4ad46dc2024-06-20 19:22:0954 j_class_name.obj()));
Clark DuVallc131c502020-11-26 16:23:4955}
56
Sam Maier06caed52024-01-25 17:10:4657// Must be called before using GetClassFromSplit - we need to set the global,
58// and we need to call GetClassLoader at least once to allow the default
59// resolver (env->FindClass()) to get our main ClassLoader class instance, which
60// we then cache use for all future calls to GetSplitClassLoader.
61void PrepareClassLoaders(JNIEnv* env) {
62 if (g_class_loader_load_class_method_id == nullptr) {
63 GetClassLoader(env);
64 ScopedJavaLocalRef<jclass> class_loader_clazz = ScopedJavaLocalRef<jclass>(
65 env, env->FindClass("java/lang/ClassLoader"));
66 CHECK(!ClearException(env));
67 g_class_loader_load_class_method_id =
68 env->GetMethodID(class_loader_clazz.obj(), "loadClass",
69 "(Ljava/lang/String;)Ljava/lang/Class;");
70 CHECK(!ClearException(env));
71 }
72}
Andrew Grieve2f99f2872024-02-07 17:41:5673#endif // !BUILDFLAG(IS_ROBOLECTRIC)
Clark DuVallc131c502020-11-26 16:23:4974} // namespace
[email protected]61c86c62011-08-02 16:11:1675
Andrew Grieve378282f2024-01-10 16:18:4976LogFatalCallback g_log_fatal_callback_for_testing = nullptr;
Andrew Grieve6230a512023-12-11 16:47:2177const char kUnableToGetStackTraceMessage[] =
78 "Unable to retrieve Java caller stack trace as the exception handler is "
79 "being re-entered";
80const char kReetrantOutOfMemoryMessage[] =
81 "While handling an uncaught Java exception, an OutOfMemoryError "
82 "occurred.";
83const char kReetrantExceptionMessage[] =
84 "While handling an uncaught Java exception, another exception "
85 "occurred.";
86const char kUncaughtExceptionMessage[] =
87 "Uncaught Java exception in native code. Please include the Java exception "
88 "stack from the Android log in your crash report.";
89const char kUncaughtExceptionHandlerFailedMessage[] =
90 "Uncaught Java exception in native code and the Java uncaught exception "
91 "handler did not terminate the process. Please include the Java exception "
92 "stack from the Android log in your crash report.";
93const char kOomInGetJavaExceptionInfoMessage[] =
94 "Unable to obtain Java stack trace due to OutOfMemoryError";
95
[email protected]61c86c62011-08-02 16:11:1696void InitVM(JavaVM* vm) {
Sam Maiere2b94c82023-12-20 19:36:1897 jni_zero::InitVM(vm);
Sam Maier79cf2fb2023-12-21 19:04:1498 jni_zero::SetExceptionHandler(CheckException);
Sam Maiercb004e132023-12-21 21:28:3599 JNIEnv* env = jni_zero::AttachCurrentThread();
Andrew Grieve2f99f2872024-02-07 17:41:56100#if !BUILDFLAG(IS_ROBOLECTRIC)
101 // Warm-up needed for GetClassFromSplit, must be called before we set the
Sam Maier06caed52024-01-25 17:10:46102 // resolver, since GetClassFromSplit won't work until after
103 // PrepareClassLoaders has happened.
104 PrepareClassLoaders(env);
105 jni_zero::SetClassResolver(GetClassFromSplit);
Andrew Grieve2f99f2872024-02-07 17:41:56106#endif
Andrew Grieve0e0938a72023-11-28 14:58:27107 g_out_of_memory_error_class = static_cast<jclass>(
108 env->NewGlobalRef(env->FindClass("java/lang/OutOfMemoryError")));
109 DCHECK(g_out_of_memory_error_class);
[email protected]61c86c62011-08-02 16:11:16110}
111
[email protected]fe0f1ab2012-02-09 21:02:27112void CheckException(JNIEnv* env) {
Sam Maier93fb22782024-01-09 16:39:25113 if (!jni_zero::HasException(env)) {
[email protected]909193c2014-06-28 01:58:04114 return;
Sam Maier93fb22782024-01-09 16:39:25115 }
[email protected]1b46a532012-05-23 05:59:49116
Etienne Dechamps8af082f82023-11-03 14:30:52117 static thread_local bool g_reentering = false;
118 if (g_reentering) {
119 // We were handling an uncaught Java exception already, but one of the Java
120 // methods we called below threw another exception. (This is unlikely to
Andrew Grieve6230a512023-12-11 16:47:21121 // happen, as we are careful to never throw from these methods, but we
122 // can't rule it out entirely. E.g. an OutOfMemoryError when constructing
123 // the jstring for the return value of
124 // sanitizedStacktraceForUnhandledException().
Andrew Grieve0e0938a72023-11-28 14:58:27125 env->ExceptionDescribe();
126 jthrowable raw_throwable = env->ExceptionOccurred();
127 env->ExceptionClear();
128 jclass clazz = env->GetObjectClass(raw_throwable);
129 bool is_oom_error = env->IsSameObject(clazz, g_out_of_memory_error_class);
130 env->Throw(raw_throwable); // Ensure we don't re-enter Java.
131
132 if (is_oom_error) {
Andrew Grieve6230a512023-12-11 16:47:21133 base::android::SetJavaException(kReetrantOutOfMemoryMessage);
Andrew Grieve0e0938a72023-11-28 14:58:27134 // Use different LOG(FATAL) statements to ensure unique stack traces.
Andrew Grieve378282f2024-01-10 16:18:49135 if (g_log_fatal_callback_for_testing) {
136 g_log_fatal_callback_for_testing(kReetrantOutOfMemoryMessage);
137 } else {
138 LOG(FATAL) << kReetrantOutOfMemoryMessage;
139 }
Andrew Grieve0e0938a72023-11-28 14:58:27140 } else {
Andrew Grieve6230a512023-12-11 16:47:21141 base::android::SetJavaException(kReetrantExceptionMessage);
Andrew Grieve378282f2024-01-10 16:18:49142 if (g_log_fatal_callback_for_testing) {
143 g_log_fatal_callback_for_testing(kReetrantExceptionMessage);
144 } else {
145 LOG(FATAL) << kReetrantExceptionMessage;
146 }
Andrew Grieve0e0938a72023-11-28 14:58:27147 }
Andrew Grieve6230a512023-12-11 16:47:21148 // Needed for tests, which do not terminate from LOG(FATAL).
149 return;
Etienne Dechamps8af082f82023-11-03 14:30:52150 }
151 g_reentering = true;
152
153 // Log a message to ensure there is something in the log even if the rest of
154 // this function goes horribly wrong, and also to provide a convenient marker
155 // in the log for where Java exception crash information starts.
156 LOG(ERROR) << "Crashing due to uncaught Java exception";
157
158 const bool handle_exception_in_java =
159 base::FeatureList::IsEnabled(kHandleExceptionsInJava);
160
161 if (!handle_exception_in_java) {
162 env->ExceptionDescribe();
163 }
164
Etienne Dechamps0ff37d62023-11-02 17:22:53165 // We cannot use `ScopedJavaLocalRef` directly because that ends up calling
166 // env->GetObjectRefType() when DCHECK is on, and that call is not allowed
167 // with a pending exception according to the JNI spec.
168 jthrowable raw_throwable = env->ExceptionOccurred();
Etienne Dechamps8af082f82023-11-03 14:30:52169 // Now that we saved the reference to the throwable, clear the exception.
170 //
171 // We need to do this as early as possible to remove the risk that code below
172 // might accidentally call back into Java, which is not allowed when `env`
173 // has an exception set, per the JNI spec. (For example, LOG(FATAL) doesn't
174 // work with a JNI exception set, because it calls
175 // GetJavaStackTraceIfPresent()).
176 env->ExceptionClear();
177 // The reference returned by `ExceptionOccurred()` is a local reference.
178 // `ExceptionClear()` merely removes the exception information from `env`;
179 // it doesn't delete the reference, which is why this call is valid.
180 auto throwable = ScopedJavaLocalRef<jthrowable>::Adopt(env, raw_throwable);
[email protected]909193c2014-06-28 01:58:04181
Etienne Dechamps4ebe3892023-11-03 18:02:12182 if (!handle_exception_in_java) {
183 base::android::SetJavaException(
184 GetJavaExceptionInfo(env, throwable).c_str());
Andrew Grieve378282f2024-01-10 16:18:49185 if (g_log_fatal_callback_for_testing) {
186 g_log_fatal_callback_for_testing(kUncaughtExceptionMessage);
187 } else {
188 LOG(FATAL) << kUncaughtExceptionMessage;
189 }
Andrew Grieve6230a512023-12-11 16:47:21190 // Needed for tests, which do not terminate from LOG(FATAL).
191 g_reentering = false;
192 return;
Etienne Dechamps4ebe3892023-11-03 18:02:12193 }
[email protected]1b46a532012-05-23 05:59:49194
Etienne Dechamps4ebe3892023-11-03 18:02:12195 // We don't need to call SetJavaException() in this branch because we
196 // expect handleException() to eventually call JavaExceptionReporter through
197 // the global uncaught exception handler.
Etienne Dechamps8af082f82023-11-03 14:30:52198
Etienne Dechampse4321c22023-11-03 15:35:54199 const std::string native_stack_trace = base::debug::StackTrace().ToString();
200 LOG(ERROR) << "Native stack trace:" << std::endl << native_stack_trace;
201
Andrew Grieve77309352023-11-27 21:47:51202 ScopedJavaLocalRef<jthrowable> secondary_exception =
Glenn Hartmanndf502162024-12-17 20:13:22203 Java_JniAndroid_handleException(env, throwable, native_stack_trace);
Etienne Dechamps8af082f82023-11-03 14:30:52204
205 // Ideally handleException() should have terminated the process and we should
Andrew Grieve77309352023-11-27 21:47:51206 // not get here. This can happen in the case of OutOfMemoryError or if the
207 // app that embedded WebView installed an exception handler that does not
208 // terminate, or itself threw an exception. We cannot be confident that
209 // JavaExceptionReporter ran, so set the java exception explicitly.
210 base::android::SetJavaException(
211 GetJavaExceptionInfo(
212 env, secondary_exception ? secondary_exception : throwable)
213 .c_str());
Andrew Grieve378282f2024-01-10 16:18:49214 if (g_log_fatal_callback_for_testing) {
215 g_log_fatal_callback_for_testing(kUncaughtExceptionHandlerFailedMessage);
216 } else {
217 LOG(FATAL) << kUncaughtExceptionHandlerFailedMessage;
218 }
Andrew Grieve6230a512023-12-11 16:47:21219 // Needed for tests, which do not terminate from LOG(FATAL).
220 g_reentering = false;
[email protected]fe0f1ab2012-02-09 21:02:27221}
222
Andrew Grieved03f4ca2023-09-20 19:20:54223std::string GetJavaExceptionInfo(JNIEnv* env,
224 const JavaRef<jthrowable>& throwable) {
Glenn Hartmanndf502162024-12-17 20:13:22225 std::string sanitized_exception_string =
Etienne Dechamps8af082f82023-11-03 14:30:52226 Java_JniAndroid_sanitizedStacktraceForUnhandledException(env, throwable);
Andrew Grieve0e0938a72023-11-28 14:58:27227 // Returns null when PiiElider results in an OutOfMemoryError.
Glenn Hartmanndf502162024-12-17 20:13:22228 return !sanitized_exception_string.empty()
229 ? sanitized_exception_string
Andrew Grieve6230a512023-12-11 16:47:21230 : kOomInGetJavaExceptionInfoMessage;
cjhopman060e0072015-05-06 21:37:48231}
232
Andrew Grieved03f4ca2023-09-20 19:20:54233std::string GetJavaStackTraceIfPresent() {
234 JNIEnv* env = nullptr;
Sam Maiercb004e132023-12-21 21:28:35235 JavaVM* jvm = jni_zero::GetVM();
236 if (jvm) {
237 jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
Andrew Grieved03f4ca2023-09-20 19:20:54238 }
239 if (!env) {
240 // JNI has not been initialized on this thread.
241 return {};
242 }
Etienne Dechamps8af082f82023-11-03 14:30:52243
244 if (HasException(env)) {
245 // This can happen if CheckException() is being re-entered, decided to
246 // LOG(FATAL) immediately, and LOG(FATAL) itself is calling us. In that case
247 // it is imperative that we don't try to call Java again.
Andrew Grieve6230a512023-12-11 16:47:21248 return kUnableToGetStackTraceMessage;
Etienne Dechamps8af082f82023-11-03 14:30:52249 }
250
Andrew Grieved03f4ca2023-09-20 19:20:54251 ScopedJavaLocalRef<jthrowable> throwable =
252 JNI_Throwable::Java_Throwable_Constructor(env);
253 std::string ret = GetJavaExceptionInfo(env, throwable);
254 // Strip the exception message and leave only the "at" lines. Example:
255 // java.lang.Throwable:
256 // {tab}at Clazz.method(Clazz.java:111)
257 // {tab}at ...
258 size_t newline_idx = ret.find('\n');
259 if (newline_idx == std::string::npos) {
260 // There are no java frames.
261 return {};
262 }
263 return ret.substr(newline_idx + 1);
264}
265
[email protected]61c86c62011-08-02 16:11:16266} // namespace android
267} // namespace base
Andrew Grieve48571812025-02-19 18:21:35268
269DEFINE_JNI_FOR_JniAndroid()