blob: 758ed06992f66ef9986346275d897bbfc267b530 [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"
[email protected]61c86c62011-08-02 16:11:1620
Clark DuVallc131c502020-11-26 16:23:4921namespace base {
22namespace android {
[email protected]61c86c62011-08-02 16:11:1623namespace {
[email protected]fe0f1ab2012-02-09 21:02:2724
Etienne Dechamps8af082f82023-11-03 14:30:5225// If disabled, we LOG(FATAL) immediately in native code when faced with an
26// uncaught Java exception (historical behavior). If enabled, we give the Java
27// uncaught exception handler a chance to handle the exception first, so that
28// the crash is (hopefully) seen as a Java crash, not a native crash.
29// TODO(https://crbug.com/1426888): remove this switch once we are confident the
30// new behavior is fine.
31BASE_FEATURE(kHandleExceptionsInJava,
32 "HandleJniExceptionsInJava",
33 base::FEATURE_ENABLED_BY_DEFAULT);
34
Andrew Grieve75cd62b2022-10-18 20:39:5035JavaVM* g_jvm = nullptr;
Andrew Grieve14eee892022-11-29 18:09:0336jobject g_class_loader = nullptr;
[email protected]d30dd6e2014-08-21 16:37:3237jmethodID g_class_loader_load_class_method_id = 0;
[email protected]96e7ade2011-12-05 14:42:0838
Clark DuVallc131c502020-11-26 16:23:4939ScopedJavaLocalRef<jclass> GetClassInternal(JNIEnv* env,
40 const char* class_name,
41 jobject class_loader) {
42 jclass clazz;
43 if (class_loader != nullptr) {
44 // ClassLoader.loadClass expects a classname with components separated by
45 // dots instead of the slashes that JNIEnv::FindClass expects. The JNI
46 // generator generates names with slashes, so we have to replace them here.
47 // TODO(torne): move to an approach where we always use ClassLoader except
48 // for the special case of base::android::GetClassLoader(), and change the
49 // JNI generator to generate dot-separated names. http://crbug.com/461773
50 size_t bufsize = strlen(class_name) + 1;
51 char dotted_name[bufsize];
52 memmove(dotted_name, class_name, bufsize);
53 for (size_t i = 0; i < bufsize; ++i) {
54 if (dotted_name[i] == '/') {
55 dotted_name[i] = '.';
56 }
57 }
58
59 clazz = static_cast<jclass>(
60 env->CallObjectMethod(class_loader, g_class_loader_load_class_method_id,
61 ConvertUTF8ToJavaString(env, dotted_name).obj()));
62 } else {
63 clazz = env->FindClass(class_name);
64 }
65 if (ClearException(env) || !clazz) {
66 LOG(FATAL) << "Failed to find class " << class_name;
67 }
68 return ScopedJavaLocalRef<jclass>(env, clazz);
69}
70
71} // namespace
[email protected]61c86c62011-08-02 16:11:1672
73JNIEnv* AttachCurrentThread() {
[email protected]7a3b0e42012-10-09 19:43:1074 DCHECK(g_jvm);
erikchen011ad3f2018-01-26 17:54:5575 JNIEnv* env = nullptr;
76 jint ret = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
77 if (ret == JNI_EDETACHED || !env) {
78 JavaVMAttachArgs args;
79 args.version = JNI_VERSION_1_2;
80 args.group = nullptr;
81
82 // 16 is the maximum size for thread names on Android.
83 char thread_name[16];
84 int err = prctl(PR_GET_NAME, thread_name);
85 if (err < 0) {
86 DPLOG(ERROR) << "prctl(PR_GET_NAME)";
87 args.name = nullptr;
88 } else {
89 args.name = thread_name;
90 }
91
Andrew Grieve75cd62b2022-10-18 20:39:5092#if BUILDFLAG(IS_ANDROID)
erikchen011ad3f2018-01-26 17:54:5593 ret = g_jvm->AttachCurrentThread(&env, &args);
Andrew Grieve75cd62b2022-10-18 20:39:5094#else
95 ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
96#endif
agrieve35468542018-11-30 17:59:1097 CHECK_EQ(JNI_OK, ret);
erikchen011ad3f2018-01-26 17:54:5598 }
[email protected]61c86c62011-08-02 16:11:1699 return env;
100}
101
[email protected]f3225bdb2014-06-20 00:38:19102JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
103 DCHECK(g_jvm);
104 JavaVMAttachArgs args;
105 args.version = JNI_VERSION_1_2;
Andrew Grieve75cd62b2022-10-18 20:39:50106 args.name = const_cast<char*>(thread_name.c_str());
107 args.group = nullptr;
108 JNIEnv* env = nullptr;
109#if BUILDFLAG(IS_ANDROID)
[email protected]f3225bdb2014-06-20 00:38:19110 jint ret = g_jvm->AttachCurrentThread(&env, &args);
Andrew Grieve75cd62b2022-10-18 20:39:50111#else
112 jint ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
113#endif
agrieve35468542018-11-30 17:59:10114 CHECK_EQ(JNI_OK, ret);
[email protected]f3225bdb2014-06-20 00:38:19115 return env;
116}
117
[email protected]61c86c62011-08-02 16:11:16118void DetachFromVM() {
119 // Ignore the return value, if the thread is not attached, DetachCurrentThread
120 // will fail. But it is ok as the native thread may never be attached.
[email protected]691b2002014-01-07 19:51:37121 if (g_jvm)
[email protected]61c86c62011-08-02 16:11:16122 g_jvm->DetachCurrentThread();
123}
124
125void InitVM(JavaVM* vm) {
michaelbai25ec25c2015-10-22 19:40:22126 DCHECK(!g_jvm || g_jvm == vm);
[email protected]61c86c62011-08-02 16:11:16127 g_jvm = vm;
128}
129
[email protected]2e944632013-08-21 17:59:42130bool IsVMInitialized() {
Andrew Grieve75cd62b2022-10-18 20:39:50131 return g_jvm != nullptr;
[email protected]2e944632013-08-21 17:59:42132}
133
Alex Cooperbdd9a762023-01-24 23:29:54134JavaVM* GetVM() {
135 return g_jvm;
136}
137
Andrew Grieved03f4ca2023-09-20 19:20:54138void DisableJvmForTesting() {
139 g_jvm = nullptr;
140}
141
Andrew Grieve14eee892022-11-29 18:09:03142void InitGlobalClassLoader(JNIEnv* env) {
143 DCHECK(g_class_loader == nullptr);
[email protected]d30dd6e2014-08-21 16:37:32144
145 ScopedJavaLocalRef<jclass> class_loader_clazz =
146 GetClass(env, "java/lang/ClassLoader");
147 CHECK(!ClearException(env));
148 g_class_loader_load_class_method_id =
149 env->GetMethodID(class_loader_clazz.obj(),
150 "loadClass",
151 "(Ljava/lang/String;)Ljava/lang/Class;");
152 CHECK(!ClearException(env));
153
Andrew Grieve14eee892022-11-29 18:09:03154 // GetClassLoader() caches the reference, so we do not need to wrap it in a
155 // smart pointer as well.
156 g_class_loader = GetClassLoader(env);
[email protected]d30dd6e2014-08-21 16:37:32157}
158
Clark DuVallc131c502020-11-26 16:23:49159ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
160 const char* class_name,
Andrew Grieve14eee892022-11-29 18:09:03161 const char* split_name) {
Clark DuVallc131c502020-11-26 16:23:49162 return GetClassInternal(env, class_name,
Andrew Grieve14eee892022-11-29 18:09:03163 GetSplitClassLoader(env, split_name));
[email protected]fe0f1ab2012-02-09 21:02:27164}
165
Clark DuVallc131c502020-11-26 16:23:49166ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
Andrew Grieve14eee892022-11-29 18:09:03167 return GetClassInternal(env, class_name, g_class_loader);
Clark DuVallc131c502020-11-26 16:23:49168}
169
170// This is duplicated with LazyGetClass below because these are performance
171// sensitive.
172jclass LazyGetClass(JNIEnv* env,
173 const char* class_name,
Andrew Grieve14eee892022-11-29 18:09:03174 const char* split_name,
Clark DuVallc131c502020-11-26 16:23:49175 std::atomic<jclass>* atomic_class_id) {
Benoit Lizeab965bc2021-04-01 15:30:24176 const jclass value = atomic_class_id->load(std::memory_order_acquire);
Clark DuVallc131c502020-11-26 16:23:49177 if (value)
178 return value;
179 ScopedJavaGlobalRef<jclass> clazz;
180 clazz.Reset(GetClass(env, class_name, split_name));
181 jclass cas_result = nullptr;
Benoit Lizeab965bc2021-04-01 15:30:24182 if (atomic_class_id->compare_exchange_strong(cas_result, clazz.obj(),
183 std::memory_order_acq_rel)) {
Clark DuVallc131c502020-11-26 16:23:49184 // We intentionally leak the global ref since we now storing it as a raw
185 // pointer in |atomic_class_id|.
186 return clazz.Release();
187 } else {
188 return cas_result;
189 }
190}
191
192// This is duplicated with LazyGetClass above because these are performance
193// sensitive.
194jclass LazyGetClass(JNIEnv* env,
195 const char* class_name,
196 std::atomic<jclass>* atomic_class_id) {
Benoit Lizeab965bc2021-04-01 15:30:24197 const jclass value = atomic_class_id->load(std::memory_order_acquire);
[email protected]d30dd6e2014-08-21 16:37:32198 if (value)
Oleh Prypin54f759312018-08-02 14:33:32199 return value;
[email protected]d30dd6e2014-08-21 16:37:32200 ScopedJavaGlobalRef<jclass> clazz;
201 clazz.Reset(GetClass(env, class_name));
Oleh Prypin54f759312018-08-02 14:33:32202 jclass cas_result = nullptr;
Benoit Lizeab965bc2021-04-01 15:30:24203 if (atomic_class_id->compare_exchange_strong(cas_result, clazz.obj(),
204 std::memory_order_acq_rel)) {
[email protected]d30dd6e2014-08-21 16:37:32205 // We intentionally leak the global ref since we now storing it as a raw
206 // pointer in |atomic_class_id|.
207 return clazz.Release();
208 } else {
Oleh Prypin54f759312018-08-02 14:33:32209 return cas_result;
[email protected]d30dd6e2014-08-21 16:37:32210 }
211}
212
[email protected]c410a2cb2012-10-16 18:35:10213template<MethodID::Type type>
214jmethodID MethodID::Get(JNIEnv* env,
215 jclass clazz,
216 const char* method_name,
217 const char* jni_signature) {
Oleh Prypin54f759312018-08-02 14:33:32218 auto get_method_ptr = type == MethodID::TYPE_STATIC ?
219 &JNIEnv::GetStaticMethodID :
220 &JNIEnv::GetMethodID;
221 jmethodID id = (env->*get_method_ptr)(clazz, method_name, jni_signature);
yfriedmane524fe72016-05-05 19:51:38222 if (base::android::ClearException(env) || !id) {
223 LOG(FATAL) << "Failed to find " <<
224 (type == TYPE_STATIC ? "static " : "") <<
225 "method " << method_name << " " << jni_signature;
226 }
[email protected]c410a2cb2012-10-16 18:35:10227 return id;
[email protected]fae37d62012-03-08 12:39:13228}
229
[email protected]c410a2cb2012-10-16 18:35:10230// If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
231// into ::Get() above. If there's a race, it's ok since the values are the same
232// (and the duplicated effort will happen only once).
Benoit Lizeab965bc2021-04-01 15:30:24233template <MethodID::Type type>
[email protected]c410a2cb2012-10-16 18:35:10234jmethodID MethodID::LazyGet(JNIEnv* env,
[email protected]3764dddb2012-10-02 21:13:55235 jclass clazz,
236 const char* method_name,
[email protected]c410a2cb2012-10-16 18:35:10237 const char* jni_signature,
Oleh Prypin54f759312018-08-02 14:33:32238 std::atomic<jmethodID>* atomic_method_id) {
Benoit Lizeab965bc2021-04-01 15:30:24239 const jmethodID value = atomic_method_id->load(std::memory_order_acquire);
[email protected]c410a2cb2012-10-16 18:35:10240 if (value)
Oleh Prypin54f759312018-08-02 14:33:32241 return value;
[email protected]c410a2cb2012-10-16 18:35:10242 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
Benoit Lizeab965bc2021-04-01 15:30:24243 atomic_method_id->store(id, std::memory_order_release);
[email protected]c410a2cb2012-10-16 18:35:10244 return id;
[email protected]fe0f1ab2012-02-09 21:02:27245}
246
[email protected]c410a2cb2012-10-16 18:35:10247// Various template instantiations.
248template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
249 JNIEnv* env, jclass clazz, const char* method_name,
250 const char* jni_signature);
[email protected]fae37d62012-03-08 12:39:13251
[email protected]c410a2cb2012-10-16 18:35:10252template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
253 JNIEnv* env, jclass clazz, const char* method_name,
254 const char* jni_signature);
[email protected]3764dddb2012-10-02 21:13:55255
[email protected]c410a2cb2012-10-16 18:35:10256template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
257 JNIEnv* env, jclass clazz, const char* method_name,
Oleh Prypin54f759312018-08-02 14:33:32258 const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
[email protected]fe0f1ab2012-02-09 21:02:27259
[email protected]c410a2cb2012-10-16 18:35:10260template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
261 JNIEnv* env, jclass clazz, const char* method_name,
Oleh Prypin54f759312018-08-02 14:33:32262 const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
[email protected]fe0f1ab2012-02-09 21:02:27263
[email protected]fe0f1ab2012-02-09 21:02:27264bool HasException(JNIEnv* env) {
265 return env->ExceptionCheck() != JNI_FALSE;
[email protected]61c86c62011-08-02 16:11:16266}
267
[email protected]fe0f1ab2012-02-09 21:02:27268bool ClearException(JNIEnv* env) {
269 if (!HasException(env))
[email protected]61c86c62011-08-02 16:11:16270 return false;
[email protected]1b46a532012-05-23 05:59:49271 env->ExceptionDescribe();
[email protected]61c86c62011-08-02 16:11:16272 env->ExceptionClear();
273 return true;
274}
275
[email protected]fe0f1ab2012-02-09 21:02:27276void CheckException(JNIEnv* env) {
[email protected]909193c2014-06-28 01:58:04277 if (!HasException(env))
278 return;
[email protected]1b46a532012-05-23 05:59:49279
Etienne Dechamps8af082f82023-11-03 14:30:52280 static thread_local bool g_reentering = false;
281 if (g_reentering) {
282 // We were handling an uncaught Java exception already, but one of the Java
283 // methods we called below threw another exception. (This is unlikely to
284 // happen as we are careful to never throw from these methods, but we can't
285 // rule it out entirely as the JVM itself may throw - think
286 // OutOfMemoryError, for example.)
287 //
288 // Note that just because we LOG(FATAL) here does not mean it's over -
289 // indeed LOG(FATAL) itself may attempt to call Java methods (e.g. through
290 // GetJavaStackTraceIfPresent()), and we can't let that happen because that
291 // could lead to infinite recursion. To prevent this, we deliberately
292 // refrain from calling `env->ExceptionClear()` in this case - this way, the
293 // JVM will instantly crash if called again from this thread. Such crashes
294 // are hard to troubleshoot though, so ideally attempts to call Java methods
295 // from LOG(FATAL) should guard against HasException() to provide a better
296 // message.
297 constexpr char kMessage[] =
298 "While handling an uncaught Java exception, another Java exception was "
299 "thrown (out of memory?).";
300 base::android::SetJavaException(kMessage);
301 LOG(FATAL) << kMessage;
302 }
303 g_reentering = true;
304
305 // Log a message to ensure there is something in the log even if the rest of
306 // this function goes horribly wrong, and also to provide a convenient marker
307 // in the log for where Java exception crash information starts.
308 LOG(ERROR) << "Crashing due to uncaught Java exception";
309
310 const bool handle_exception_in_java =
311 base::FeatureList::IsEnabled(kHandleExceptionsInJava);
312
313 if (!handle_exception_in_java) {
314 env->ExceptionDescribe();
315 }
316
Etienne Dechamps0ff37d62023-11-02 17:22:53317 // We cannot use `ScopedJavaLocalRef` directly because that ends up calling
318 // env->GetObjectRefType() when DCHECK is on, and that call is not allowed
319 // with a pending exception according to the JNI spec.
320 jthrowable raw_throwable = env->ExceptionOccurred();
Etienne Dechamps8af082f82023-11-03 14:30:52321 // Now that we saved the reference to the throwable, clear the exception.
322 //
323 // We need to do this as early as possible to remove the risk that code below
324 // might accidentally call back into Java, which is not allowed when `env`
325 // has an exception set, per the JNI spec. (For example, LOG(FATAL) doesn't
326 // work with a JNI exception set, because it calls
327 // GetJavaStackTraceIfPresent()).
328 env->ExceptionClear();
329 // The reference returned by `ExceptionOccurred()` is a local reference.
330 // `ExceptionClear()` merely removes the exception information from `env`;
331 // it doesn't delete the reference, which is why this call is valid.
332 auto throwable = ScopedJavaLocalRef<jthrowable>::Adopt(env, raw_throwable);
[email protected]909193c2014-06-28 01:58:04333
Etienne Dechamps4ebe3892023-11-03 18:02:12334 if (!handle_exception_in_java) {
335 base::android::SetJavaException(
336 GetJavaExceptionInfo(env, throwable).c_str());
337 LOG(FATAL)
338 << "Uncaught Java exception in native code. Please include the Java "
339 "exception stack from the Android log in your crash report.";
340 }
[email protected]1b46a532012-05-23 05:59:49341
Etienne Dechamps4ebe3892023-11-03 18:02:12342 // We don't need to call SetJavaException() in this branch because we
343 // expect handleException() to eventually call JavaExceptionReporter through
344 // the global uncaught exception handler.
Etienne Dechamps8af082f82023-11-03 14:30:52345
Etienne Dechampse4321c22023-11-03 15:35:54346 const std::string native_stack_trace = base::debug::StackTrace().ToString();
347 LOG(ERROR) << "Native stack trace:" << std::endl << native_stack_trace;
348
349 Java_JniAndroid_handleException(
350 env, throwable, ConvertUTF8ToJavaString(env, native_stack_trace));
Etienne Dechamps8af082f82023-11-03 14:30:52351
352 // Ideally handleException() should have terminated the process and we should
353 // not get here. In the unlikely case it didn't, we need to do that ourselves.
354 LOG(FATAL)
355 << "Uncaught Java exception in native code, and the Java uncaught "
356 "exception handler did not terminate the process. Please include the "
357 "Java exception stack from the Android log in your crash report.";
[email protected]fe0f1ab2012-02-09 21:02:27358}
359
Andrew Grieved03f4ca2023-09-20 19:20:54360std::string GetJavaExceptionInfo(JNIEnv* env,
361 const JavaRef<jthrowable>& throwable) {
Dan Stahr017ceaf2023-01-12 09:32:17362 ScopedJavaLocalRef<jstring> sanitized_exception_string =
Etienne Dechamps8af082f82023-11-03 14:30:52363 Java_JniAndroid_sanitizedStacktraceForUnhandledException(env, throwable);
Mohamed Heikal47fa68712018-11-14 23:54:44364
365 return ConvertJavaStringToUTF8(sanitized_exception_string);
cjhopman060e0072015-05-06 21:37:48366}
367
Andrew Grieved03f4ca2023-09-20 19:20:54368std::string GetJavaStackTraceIfPresent() {
369 JNIEnv* env = nullptr;
370 if (g_jvm) {
371 g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
372 }
373 if (!env) {
374 // JNI has not been initialized on this thread.
375 return {};
376 }
Etienne Dechamps8af082f82023-11-03 14:30:52377
378 if (HasException(env)) {
379 // This can happen if CheckException() is being re-entered, decided to
380 // LOG(FATAL) immediately, and LOG(FATAL) itself is calling us. In that case
381 // it is imperative that we don't try to call Java again.
382 return "Unable to retrieve Java caller stack trace as the exception "
383 "handler is being re-entered";
384 }
385
Andrew Grieved03f4ca2023-09-20 19:20:54386 ScopedJavaLocalRef<jthrowable> throwable =
387 JNI_Throwable::Java_Throwable_Constructor(env);
388 std::string ret = GetJavaExceptionInfo(env, throwable);
389 // Strip the exception message and leave only the "at" lines. Example:
390 // java.lang.Throwable:
391 // {tab}at Clazz.method(Clazz.java:111)
392 // {tab}at ...
393 size_t newline_idx = ret.find('\n');
394 if (newline_idx == std::string::npos) {
395 // There are no java frames.
396 return {};
397 }
398 return ret.substr(newline_idx + 1);
399}
400
[email protected]61c86c62011-08-02 16:11:16401} // namespace android
402} // namespace base