blob: 4d546472066ffbec40332f0784fa9ada641b21af [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"
Etienne Dechamps8af082f82023-11-03 14:30:5214#include "base/base_jni/JniAndroid_jni.h"
Scott Violet44165792018-02-22 02:08:0815#include "base/debug/debugging_buildflags.h"
Etienne Dechamps8af082f82023-11-03 14:30:5216#include "base/feature_list.h"
[email protected]61c86c62011-08-02 16:11:1617#include "base/logging.h"
Andrew Grieve75cd62b2022-10-18 20:39:5018#include "build/build_config.h"
Peter Kastingdad508e42023-03-13 16:12:5519#include "third_party/abseil-cpp/absl/base/attributes.h"
Sam Maiere2b94c82023-12-20 19:36:1820#include "third_party/jni_zero/core.h"
[email protected]61c86c62011-08-02 16:11:1621
Clark DuVallc131c502020-11-26 16:23:4922namespace base {
23namespace android {
[email protected]61c86c62011-08-02 16:11:1624namespace {
[email protected]fe0f1ab2012-02-09 21:02:2725
Etienne Dechamps8af082f82023-11-03 14:30:5226// If disabled, we LOG(FATAL) immediately in native code when faced with an
27// uncaught Java exception (historical behavior). If enabled, we give the Java
28// uncaught exception handler a chance to handle the exception first, so that
29// the crash is (hopefully) seen as a Java crash, not a native crash.
30// TODO(https://crbug.com/1426888): remove this switch once we are confident the
31// new behavior is fine.
32BASE_FEATURE(kHandleExceptionsInJava,
33 "HandleJniExceptionsInJava",
34 base::FEATURE_ENABLED_BY_DEFAULT);
35
Andrew Grieve0e0938a72023-11-28 14:58:2736jclass g_out_of_memory_error_class = nullptr;
37jmethodID g_class_loader_load_class_method_id = nullptr;
[email protected]96e7ade2011-12-05 14:42:0838
Sam Maier06caed52024-01-25 17:10:4639jclass GetClassFromSplit(JNIEnv* env,
40 const char* class_name,
41 const char* split_name) {
42 return static_cast<jclass>(env->CallObjectMethod(
43 GetSplitClassLoader(env, split_name), g_class_loader_load_class_method_id,
44 ConvertUTF8ToJavaString(env, class_name).obj()));
Clark DuVallc131c502020-11-26 16:23:4945}
46
Sam Maier06caed52024-01-25 17:10:4647// Must be called before using GetClassFromSplit - we need to set the global,
48// and we need to call GetClassLoader at least once to allow the default
49// resolver (env->FindClass()) to get our main ClassLoader class instance, which
50// we then cache use for all future calls to GetSplitClassLoader.
51void PrepareClassLoaders(JNIEnv* env) {
52 if (g_class_loader_load_class_method_id == nullptr) {
53 GetClassLoader(env);
54 ScopedJavaLocalRef<jclass> class_loader_clazz = ScopedJavaLocalRef<jclass>(
55 env, env->FindClass("java/lang/ClassLoader"));
56 CHECK(!ClearException(env));
57 g_class_loader_load_class_method_id =
58 env->GetMethodID(class_loader_clazz.obj(), "loadClass",
59 "(Ljava/lang/String;)Ljava/lang/Class;");
60 CHECK(!ClearException(env));
61 }
62}
Clark DuVallc131c502020-11-26 16:23:4963} // namespace
[email protected]61c86c62011-08-02 16:11:1664
Andrew Grieve378282f2024-01-10 16:18:4965LogFatalCallback g_log_fatal_callback_for_testing = nullptr;
Andrew Grieve6230a512023-12-11 16:47:2166const char kUnableToGetStackTraceMessage[] =
67 "Unable to retrieve Java caller stack trace as the exception handler is "
68 "being re-entered";
69const char kReetrantOutOfMemoryMessage[] =
70 "While handling an uncaught Java exception, an OutOfMemoryError "
71 "occurred.";
72const char kReetrantExceptionMessage[] =
73 "While handling an uncaught Java exception, another exception "
74 "occurred.";
75const char kUncaughtExceptionMessage[] =
76 "Uncaught Java exception in native code. Please include the Java exception "
77 "stack from the Android log in your crash report.";
78const char kUncaughtExceptionHandlerFailedMessage[] =
79 "Uncaught Java exception in native code and the Java uncaught exception "
80 "handler did not terminate the process. Please include the Java exception "
81 "stack from the Android log in your crash report.";
82const char kOomInGetJavaExceptionInfoMessage[] =
83 "Unable to obtain Java stack trace due to OutOfMemoryError";
84
[email protected]61c86c62011-08-02 16:11:1685void InitVM(JavaVM* vm) {
Sam Maiere2b94c82023-12-20 19:36:1886 jni_zero::InitVM(vm);
Sam Maier79cf2fb2023-12-21 19:04:1487 jni_zero::SetExceptionHandler(CheckException);
Sam Maiercb004e132023-12-21 21:28:3588 JNIEnv* env = jni_zero::AttachCurrentThread();
Sam Maier06caed52024-01-25 17:10:4689 // Warmup needed for GetClassFromSplit, must be called before we set the
90 // resolver, since GetClassFromSplit won't work until after
91 // PrepareClassLoaders has happened.
92 PrepareClassLoaders(env);
93 jni_zero::SetClassResolver(GetClassFromSplit);
Andrew Grieve0e0938a72023-11-28 14:58:2794 g_out_of_memory_error_class = static_cast<jclass>(
95 env->NewGlobalRef(env->FindClass("java/lang/OutOfMemoryError")));
96 DCHECK(g_out_of_memory_error_class);
[email protected]61c86c62011-08-02 16:11:1697}
98
[email protected]c410a2cb2012-10-16 18:35:1099template<MethodID::Type type>
100jmethodID MethodID::Get(JNIEnv* env,
101 jclass clazz,
102 const char* method_name,
103 const char* jni_signature) {
Oleh Prypin54f759312018-08-02 14:33:32104 auto get_method_ptr = type == MethodID::TYPE_STATIC ?
105 &JNIEnv::GetStaticMethodID :
106 &JNIEnv::GetMethodID;
107 jmethodID id = (env->*get_method_ptr)(clazz, method_name, jni_signature);
yfriedmane524fe72016-05-05 19:51:38108 if (base::android::ClearException(env) || !id) {
109 LOG(FATAL) << "Failed to find " <<
110 (type == TYPE_STATIC ? "static " : "") <<
111 "method " << method_name << " " << jni_signature;
112 }
[email protected]c410a2cb2012-10-16 18:35:10113 return id;
[email protected]fae37d62012-03-08 12:39:13114}
115
[email protected]c410a2cb2012-10-16 18:35:10116// If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
117// into ::Get() above. If there's a race, it's ok since the values are the same
118// (and the duplicated effort will happen only once).
Benoit Lizeab965bc2021-04-01 15:30:24119template <MethodID::Type type>
[email protected]c410a2cb2012-10-16 18:35:10120jmethodID MethodID::LazyGet(JNIEnv* env,
[email protected]3764dddb2012-10-02 21:13:55121 jclass clazz,
122 const char* method_name,
[email protected]c410a2cb2012-10-16 18:35:10123 const char* jni_signature,
Oleh Prypin54f759312018-08-02 14:33:32124 std::atomic<jmethodID>* atomic_method_id) {
Benoit Lizeab965bc2021-04-01 15:30:24125 const jmethodID value = atomic_method_id->load(std::memory_order_acquire);
[email protected]c410a2cb2012-10-16 18:35:10126 if (value)
Oleh Prypin54f759312018-08-02 14:33:32127 return value;
[email protected]c410a2cb2012-10-16 18:35:10128 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
Benoit Lizeab965bc2021-04-01 15:30:24129 atomic_method_id->store(id, std::memory_order_release);
[email protected]c410a2cb2012-10-16 18:35:10130 return id;
[email protected]fe0f1ab2012-02-09 21:02:27131}
132
[email protected]c410a2cb2012-10-16 18:35:10133// Various template instantiations.
134template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
135 JNIEnv* env, jclass clazz, const char* method_name,
136 const char* jni_signature);
[email protected]fae37d62012-03-08 12:39:13137
[email protected]c410a2cb2012-10-16 18:35:10138template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
139 JNIEnv* env, jclass clazz, const char* method_name,
140 const char* jni_signature);
[email protected]3764dddb2012-10-02 21:13:55141
[email protected]c410a2cb2012-10-16 18:35:10142template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
143 JNIEnv* env, jclass clazz, const char* method_name,
Oleh Prypin54f759312018-08-02 14:33:32144 const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
[email protected]fe0f1ab2012-02-09 21:02:27145
[email protected]c410a2cb2012-10-16 18:35:10146template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
147 JNIEnv* env, jclass clazz, const char* method_name,
Oleh Prypin54f759312018-08-02 14:33:32148 const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
[email protected]fe0f1ab2012-02-09 21:02:27149
[email protected]61c86c62011-08-02 16:11:16150
[email protected]fe0f1ab2012-02-09 21:02:27151void CheckException(JNIEnv* env) {
Sam Maier93fb22782024-01-09 16:39:25152 if (!jni_zero::HasException(env)) {
[email protected]909193c2014-06-28 01:58:04153 return;
Sam Maier93fb22782024-01-09 16:39:25154 }
[email protected]1b46a532012-05-23 05:59:49155
Etienne Dechamps8af082f82023-11-03 14:30:52156 static thread_local bool g_reentering = false;
157 if (g_reentering) {
158 // We were handling an uncaught Java exception already, but one of the Java
159 // methods we called below threw another exception. (This is unlikely to
Andrew Grieve6230a512023-12-11 16:47:21160 // happen, as we are careful to never throw from these methods, but we
161 // can't rule it out entirely. E.g. an OutOfMemoryError when constructing
162 // the jstring for the return value of
163 // sanitizedStacktraceForUnhandledException().
Andrew Grieve0e0938a72023-11-28 14:58:27164 env->ExceptionDescribe();
165 jthrowable raw_throwable = env->ExceptionOccurred();
166 env->ExceptionClear();
167 jclass clazz = env->GetObjectClass(raw_throwable);
168 bool is_oom_error = env->IsSameObject(clazz, g_out_of_memory_error_class);
169 env->Throw(raw_throwable); // Ensure we don't re-enter Java.
170
171 if (is_oom_error) {
Andrew Grieve6230a512023-12-11 16:47:21172 base::android::SetJavaException(kReetrantOutOfMemoryMessage);
Andrew Grieve0e0938a72023-11-28 14:58:27173 // Use different LOG(FATAL) statements to ensure unique stack traces.
Andrew Grieve378282f2024-01-10 16:18:49174 if (g_log_fatal_callback_for_testing) {
175 g_log_fatal_callback_for_testing(kReetrantOutOfMemoryMessage);
176 } else {
177 LOG(FATAL) << kReetrantOutOfMemoryMessage;
178 }
Andrew Grieve0e0938a72023-11-28 14:58:27179 } else {
Andrew Grieve6230a512023-12-11 16:47:21180 base::android::SetJavaException(kReetrantExceptionMessage);
Andrew Grieve378282f2024-01-10 16:18:49181 if (g_log_fatal_callback_for_testing) {
182 g_log_fatal_callback_for_testing(kReetrantExceptionMessage);
183 } else {
184 LOG(FATAL) << kReetrantExceptionMessage;
185 }
Andrew Grieve0e0938a72023-11-28 14:58:27186 }
Andrew Grieve6230a512023-12-11 16:47:21187 // Needed for tests, which do not terminate from LOG(FATAL).
188 return;
Etienne Dechamps8af082f82023-11-03 14:30:52189 }
190 g_reentering = true;
191
192 // Log a message to ensure there is something in the log even if the rest of
193 // this function goes horribly wrong, and also to provide a convenient marker
194 // in the log for where Java exception crash information starts.
195 LOG(ERROR) << "Crashing due to uncaught Java exception";
196
197 const bool handle_exception_in_java =
198 base::FeatureList::IsEnabled(kHandleExceptionsInJava);
199
200 if (!handle_exception_in_java) {
201 env->ExceptionDescribe();
202 }
203
Etienne Dechamps0ff37d62023-11-02 17:22:53204 // We cannot use `ScopedJavaLocalRef` directly because that ends up calling
205 // env->GetObjectRefType() when DCHECK is on, and that call is not allowed
206 // with a pending exception according to the JNI spec.
207 jthrowable raw_throwable = env->ExceptionOccurred();
Etienne Dechamps8af082f82023-11-03 14:30:52208 // Now that we saved the reference to the throwable, clear the exception.
209 //
210 // We need to do this as early as possible to remove the risk that code below
211 // might accidentally call back into Java, which is not allowed when `env`
212 // has an exception set, per the JNI spec. (For example, LOG(FATAL) doesn't
213 // work with a JNI exception set, because it calls
214 // GetJavaStackTraceIfPresent()).
215 env->ExceptionClear();
216 // The reference returned by `ExceptionOccurred()` is a local reference.
217 // `ExceptionClear()` merely removes the exception information from `env`;
218 // it doesn't delete the reference, which is why this call is valid.
219 auto throwable = ScopedJavaLocalRef<jthrowable>::Adopt(env, raw_throwable);
[email protected]909193c2014-06-28 01:58:04220
Etienne Dechamps4ebe3892023-11-03 18:02:12221 if (!handle_exception_in_java) {
222 base::android::SetJavaException(
223 GetJavaExceptionInfo(env, throwable).c_str());
Andrew Grieve378282f2024-01-10 16:18:49224 if (g_log_fatal_callback_for_testing) {
225 g_log_fatal_callback_for_testing(kUncaughtExceptionMessage);
226 } else {
227 LOG(FATAL) << kUncaughtExceptionMessage;
228 }
Andrew Grieve6230a512023-12-11 16:47:21229 // Needed for tests, which do not terminate from LOG(FATAL).
230 g_reentering = false;
231 return;
Etienne Dechamps4ebe3892023-11-03 18:02:12232 }
[email protected]1b46a532012-05-23 05:59:49233
Etienne Dechamps4ebe3892023-11-03 18:02:12234 // We don't need to call SetJavaException() in this branch because we
235 // expect handleException() to eventually call JavaExceptionReporter through
236 // the global uncaught exception handler.
Etienne Dechamps8af082f82023-11-03 14:30:52237
Etienne Dechampse4321c22023-11-03 15:35:54238 const std::string native_stack_trace = base::debug::StackTrace().ToString();
239 LOG(ERROR) << "Native stack trace:" << std::endl << native_stack_trace;
240
Andrew Grieve77309352023-11-27 21:47:51241 ScopedJavaLocalRef<jthrowable> secondary_exception =
242 Java_JniAndroid_handleException(
243 env, throwable, ConvertUTF8ToJavaString(env, native_stack_trace));
Etienne Dechamps8af082f82023-11-03 14:30:52244
245 // Ideally handleException() should have terminated the process and we should
Andrew Grieve77309352023-11-27 21:47:51246 // not get here. This can happen in the case of OutOfMemoryError or if the
247 // app that embedded WebView installed an exception handler that does not
248 // terminate, or itself threw an exception. We cannot be confident that
249 // JavaExceptionReporter ran, so set the java exception explicitly.
250 base::android::SetJavaException(
251 GetJavaExceptionInfo(
252 env, secondary_exception ? secondary_exception : throwable)
253 .c_str());
Andrew Grieve378282f2024-01-10 16:18:49254 if (g_log_fatal_callback_for_testing) {
255 g_log_fatal_callback_for_testing(kUncaughtExceptionHandlerFailedMessage);
256 } else {
257 LOG(FATAL) << kUncaughtExceptionHandlerFailedMessage;
258 }
Andrew Grieve6230a512023-12-11 16:47:21259 // Needed for tests, which do not terminate from LOG(FATAL).
260 g_reentering = false;
[email protected]fe0f1ab2012-02-09 21:02:27261}
262
Andrew Grieved03f4ca2023-09-20 19:20:54263std::string GetJavaExceptionInfo(JNIEnv* env,
264 const JavaRef<jthrowable>& throwable) {
Dan Stahr017ceaf2023-01-12 09:32:17265 ScopedJavaLocalRef<jstring> sanitized_exception_string =
Etienne Dechamps8af082f82023-11-03 14:30:52266 Java_JniAndroid_sanitizedStacktraceForUnhandledException(env, throwable);
Andrew Grieve0e0938a72023-11-28 14:58:27267 // Returns null when PiiElider results in an OutOfMemoryError.
268 return sanitized_exception_string
269 ? ConvertJavaStringToUTF8(sanitized_exception_string)
Andrew Grieve6230a512023-12-11 16:47:21270 : kOomInGetJavaExceptionInfoMessage;
cjhopman060e0072015-05-06 21:37:48271}
272
Andrew Grieved03f4ca2023-09-20 19:20:54273std::string GetJavaStackTraceIfPresent() {
274 JNIEnv* env = nullptr;
Sam Maiercb004e132023-12-21 21:28:35275 JavaVM* jvm = jni_zero::GetVM();
276 if (jvm) {
277 jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
Andrew Grieved03f4ca2023-09-20 19:20:54278 }
279 if (!env) {
280 // JNI has not been initialized on this thread.
281 return {};
282 }
Etienne Dechamps8af082f82023-11-03 14:30:52283
284 if (HasException(env)) {
285 // This can happen if CheckException() is being re-entered, decided to
286 // LOG(FATAL) immediately, and LOG(FATAL) itself is calling us. In that case
287 // it is imperative that we don't try to call Java again.
Andrew Grieve6230a512023-12-11 16:47:21288 return kUnableToGetStackTraceMessage;
Etienne Dechamps8af082f82023-11-03 14:30:52289 }
290
Andrew Grieved03f4ca2023-09-20 19:20:54291 ScopedJavaLocalRef<jthrowable> throwable =
292 JNI_Throwable::Java_Throwable_Constructor(env);
293 std::string ret = GetJavaExceptionInfo(env, throwable);
294 // Strip the exception message and leave only the "at" lines. Example:
295 // java.lang.Throwable:
296 // {tab}at Clazz.method(Clazz.java:111)
297 // {tab}at ...
298 size_t newline_idx = ret.find('\n');
299 if (newline_idx == std::string::npos) {
300 // There are no java frames.
301 return {};
302 }
303 return ret.substr(newline_idx + 1);
304}
305
[email protected]61c86c62011-08-02 16:11:16306} // namespace android
307} // namespace base