1 // Copyright 2012 The Chromium Authors
2 // 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
7 #include <stddef.h>
8 #include <sys/prctl.h>
9
10 #include "base/android/java_exception_reporter.h"
11 #include "base/android/jni_string.h"
12 #include "base/android/jni_utils.h"
13 #include "base/base_jni_headers/PiiElider_jni.h"
14 #include "base/debug/debugging_buildflags.h"
15 #include "base/logging.h"
16 #include "build/build_config.h"
17 #include "third_party/abseil-cpp/absl/base/attributes.h"
18
19 namespace base {
20 namespace android {
21 namespace {
22
23 JavaVM* g_jvm = nullptr;
24 jobject g_class_loader = nullptr;
25 jmethodID g_class_loader_load_class_method_id = 0;
26
27 #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
28 ABSL_CONST_INIT thread_local void* stack_frame_pointer = nullptr;
29 #endif
30
31 bool g_fatal_exception_occurred = false;
32
GetClassInternal(JNIEnv * env,const char * class_name,jobject class_loader)33 ScopedJavaLocalRef<jclass> GetClassInternal(JNIEnv* env,
34 const char* class_name,
35 jobject class_loader) {
36 jclass clazz;
37 if (class_loader != nullptr) {
38 // ClassLoader.loadClass expects a classname with components separated by
39 // dots instead of the slashes that JNIEnv::FindClass expects. The JNI
40 // generator generates names with slashes, so we have to replace them here.
41 // TODO(torne): move to an approach where we always use ClassLoader except
42 // for the special case of base::android::GetClassLoader(), and change the
43 // JNI generator to generate dot-separated names. http://crbug.com/461773
44 size_t bufsize = strlen(class_name) + 1;
45 char dotted_name[bufsize];
46 memmove(dotted_name, class_name, bufsize);
47 for (size_t i = 0; i < bufsize; ++i) {
48 if (dotted_name[i] == '/') {
49 dotted_name[i] = '.';
50 }
51 }
52
53 clazz = static_cast<jclass>(
54 env->CallObjectMethod(class_loader, g_class_loader_load_class_method_id,
55 ConvertUTF8ToJavaString(env, dotted_name).obj()));
56 } else {
57 clazz = env->FindClass(class_name);
58 }
59 if (ClearException(env) || !clazz) {
60 LOG(FATAL) << "Failed to find class " << class_name;
61 }
62 return ScopedJavaLocalRef<jclass>(env, clazz);
63 }
64
65 } // namespace
66
AttachCurrentThread()67 JNIEnv* AttachCurrentThread() {
68 DCHECK(g_jvm);
69 JNIEnv* env = nullptr;
70 jint ret = g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
71 if (ret == JNI_EDETACHED || !env) {
72 JavaVMAttachArgs args;
73 args.version = JNI_VERSION_1_2;
74 args.group = nullptr;
75
76 // 16 is the maximum size for thread names on Android.
77 char thread_name[16];
78 int err = prctl(PR_GET_NAME, thread_name);
79 if (err < 0) {
80 DPLOG(ERROR) << "prctl(PR_GET_NAME)";
81 args.name = nullptr;
82 } else {
83 args.name = thread_name;
84 }
85
86 #if BUILDFLAG(IS_ANDROID)
87 ret = g_jvm->AttachCurrentThread(&env, &args);
88 #else
89 ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
90 #endif
91 CHECK_EQ(JNI_OK, ret);
92 }
93 return env;
94 }
95
AttachCurrentThreadWithName(const std::string & thread_name)96 JNIEnv* AttachCurrentThreadWithName(const std::string& thread_name) {
97 DCHECK(g_jvm);
98 JavaVMAttachArgs args;
99 args.version = JNI_VERSION_1_2;
100 args.name = const_cast<char*>(thread_name.c_str());
101 args.group = nullptr;
102 JNIEnv* env = nullptr;
103 #if BUILDFLAG(IS_ANDROID)
104 jint ret = g_jvm->AttachCurrentThread(&env, &args);
105 #else
106 jint ret = g_jvm->AttachCurrentThread(reinterpret_cast<void**>(&env), &args);
107 #endif
108 CHECK_EQ(JNI_OK, ret);
109 return env;
110 }
111
DetachFromVM()112 void DetachFromVM() {
113 // Ignore the return value, if the thread is not attached, DetachCurrentThread
114 // will fail. But it is ok as the native thread may never be attached.
115 if (g_jvm)
116 g_jvm->DetachCurrentThread();
117 }
118
InitVM(JavaVM * vm)119 void InitVM(JavaVM* vm) {
120 DCHECK(!g_jvm || g_jvm == vm);
121 g_jvm = vm;
122 }
123
IsVMInitialized()124 bool IsVMInitialized() {
125 return g_jvm != nullptr;
126 }
127
GetVM()128 JavaVM* GetVM() {
129 return g_jvm;
130 }
131
InitGlobalClassLoader(JNIEnv * env)132 void InitGlobalClassLoader(JNIEnv* env) {
133 DCHECK(g_class_loader == nullptr);
134
135 ScopedJavaLocalRef<jclass> class_loader_clazz =
136 GetClass(env, "java/lang/ClassLoader");
137 CHECK(!ClearException(env));
138 g_class_loader_load_class_method_id =
139 env->GetMethodID(class_loader_clazz.obj(),
140 "loadClass",
141 "(Ljava/lang/String;)Ljava/lang/Class;");
142 CHECK(!ClearException(env));
143
144 // GetClassLoader() caches the reference, so we do not need to wrap it in a
145 // smart pointer as well.
146 g_class_loader = GetClassLoader(env);
147 }
148
GetClass(JNIEnv * env,const char * class_name,const char * split_name)149 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env,
150 const char* class_name,
151 const char* split_name) {
152 return GetClassInternal(env, class_name,
153 GetSplitClassLoader(env, split_name));
154 }
155
GetClass(JNIEnv * env,const char * class_name)156 ScopedJavaLocalRef<jclass> GetClass(JNIEnv* env, const char* class_name) {
157 return GetClassInternal(env, class_name, g_class_loader);
158 }
159
160 // This is duplicated with LazyGetClass below because these are performance
161 // sensitive.
LazyGetClass(JNIEnv * env,const char * class_name,const char * split_name,std::atomic<jclass> * atomic_class_id)162 jclass LazyGetClass(JNIEnv* env,
163 const char* class_name,
164 const char* split_name,
165 std::atomic<jclass>* atomic_class_id) {
166 const jclass value = atomic_class_id->load(std::memory_order_acquire);
167 if (value)
168 return value;
169 ScopedJavaGlobalRef<jclass> clazz;
170 clazz.Reset(GetClass(env, class_name, split_name));
171 jclass cas_result = nullptr;
172 if (atomic_class_id->compare_exchange_strong(cas_result, clazz.obj(),
173 std::memory_order_acq_rel)) {
174 // We intentionally leak the global ref since we now storing it as a raw
175 // pointer in |atomic_class_id|.
176 return clazz.Release();
177 } else {
178 return cas_result;
179 }
180 }
181
182 // This is duplicated with LazyGetClass above because these are performance
183 // sensitive.
LazyGetClass(JNIEnv * env,const char * class_name,std::atomic<jclass> * atomic_class_id)184 jclass LazyGetClass(JNIEnv* env,
185 const char* class_name,
186 std::atomic<jclass>* atomic_class_id) {
187 const jclass value = atomic_class_id->load(std::memory_order_acquire);
188 if (value)
189 return value;
190 ScopedJavaGlobalRef<jclass> clazz;
191 clazz.Reset(GetClass(env, class_name));
192 jclass cas_result = nullptr;
193 if (atomic_class_id->compare_exchange_strong(cas_result, clazz.obj(),
194 std::memory_order_acq_rel)) {
195 // We intentionally leak the global ref since we now storing it as a raw
196 // pointer in |atomic_class_id|.
197 return clazz.Release();
198 } else {
199 return cas_result;
200 }
201 }
202
203 template<MethodID::Type type>
Get(JNIEnv * env,jclass clazz,const char * method_name,const char * jni_signature)204 jmethodID MethodID::Get(JNIEnv* env,
205 jclass clazz,
206 const char* method_name,
207 const char* jni_signature) {
208 auto get_method_ptr = type == MethodID::TYPE_STATIC ?
209 &JNIEnv::GetStaticMethodID :
210 &JNIEnv::GetMethodID;
211 jmethodID id = (env->*get_method_ptr)(clazz, method_name, jni_signature);
212 if (base::android::ClearException(env) || !id) {
213 LOG(FATAL) << "Failed to find " <<
214 (type == TYPE_STATIC ? "static " : "") <<
215 "method " << method_name << " " << jni_signature;
216 }
217 return id;
218 }
219
220 // If |atomic_method_id| set, it'll return immediately. Otherwise, it'll call
221 // into ::Get() above. If there's a race, it's ok since the values are the same
222 // (and the duplicated effort will happen only once).
223 template <MethodID::Type type>
LazyGet(JNIEnv * env,jclass clazz,const char * method_name,const char * jni_signature,std::atomic<jmethodID> * atomic_method_id)224 jmethodID MethodID::LazyGet(JNIEnv* env,
225 jclass clazz,
226 const char* method_name,
227 const char* jni_signature,
228 std::atomic<jmethodID>* atomic_method_id) {
229 const jmethodID value = atomic_method_id->load(std::memory_order_acquire);
230 if (value)
231 return value;
232 jmethodID id = MethodID::Get<type>(env, clazz, method_name, jni_signature);
233 atomic_method_id->store(id, std::memory_order_release);
234 return id;
235 }
236
237 // Various template instantiations.
238 template jmethodID MethodID::Get<MethodID::TYPE_STATIC>(
239 JNIEnv* env, jclass clazz, const char* method_name,
240 const char* jni_signature);
241
242 template jmethodID MethodID::Get<MethodID::TYPE_INSTANCE>(
243 JNIEnv* env, jclass clazz, const char* method_name,
244 const char* jni_signature);
245
246 template jmethodID MethodID::LazyGet<MethodID::TYPE_STATIC>(
247 JNIEnv* env, jclass clazz, const char* method_name,
248 const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
249
250 template jmethodID MethodID::LazyGet<MethodID::TYPE_INSTANCE>(
251 JNIEnv* env, jclass clazz, const char* method_name,
252 const char* jni_signature, std::atomic<jmethodID>* atomic_method_id);
253
HasException(JNIEnv * env)254 bool HasException(JNIEnv* env) {
255 return env->ExceptionCheck() != JNI_FALSE;
256 }
257
ClearException(JNIEnv * env)258 bool ClearException(JNIEnv* env) {
259 if (!HasException(env))
260 return false;
261 env->ExceptionDescribe();
262 env->ExceptionClear();
263 return true;
264 }
265
CheckException(JNIEnv * env)266 void CheckException(JNIEnv* env) {
267 if (!HasException(env))
268 return;
269
270 jthrowable java_throwable = env->ExceptionOccurred();
271 if (java_throwable) {
272 // Clear the pending exception, since a local reference is now held.
273 env->ExceptionDescribe();
274 env->ExceptionClear();
275
276 if (g_fatal_exception_occurred) {
277 // Another exception (probably OOM) occurred during GetJavaExceptionInfo.
278 base::android::SetJavaException(
279 "Java OOM'ed in exception handling, check logcat");
280 } else {
281 g_fatal_exception_occurred = true;
282 // RVO should avoid any extra copies of the exception string.
283 base::android::SetJavaException(
284 GetJavaExceptionInfo(env, java_throwable).c_str());
285 }
286 }
287
288 // Now, feel good about it and die.
289 LOG(FATAL) << "Please include Java exception stack in crash report";
290 }
291
GetJavaExceptionInfo(JNIEnv * env,jthrowable java_throwable)292 std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) {
293 ScopedJavaLocalRef<jstring> sanitized_exception_string =
294 Java_PiiElider_getSanitizedStacktrace(
295 env, ScopedJavaLocalRef(env, java_throwable));
296
297 return ConvertJavaStringToUTF8(sanitized_exception_string);
298 }
299
300 #if BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
301
JNIStackFrameSaver(void * current_fp)302 JNIStackFrameSaver::JNIStackFrameSaver(void* current_fp)
303 : resetter_(&stack_frame_pointer, current_fp) {}
304
305 JNIStackFrameSaver::~JNIStackFrameSaver() = default;
306
SavedFrame()307 void* JNIStackFrameSaver::SavedFrame() {
308 return stack_frame_pointer;
309 }
310
311 #endif // BUILDFLAG(CAN_UNWIND_WITH_FRAME_POINTERS)
312
313 } // namespace android
314 } // namespace base
315