1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "berberis/jni/jni_trampolines.h"
18
19 #include <cstdint>
20 #include <cstring>
21 #include <deque>
22 #include <map>
23 #include <mutex>
24 #include <vector>
25
26 #include <jni.h> // NOLINT [build/include_order]
27
28 #include "berberis/base/checks.h"
29 #include "berberis/base/gettid.h"
30 #include "berberis/base/logging.h"
31 #include "berberis/base/tracing.h"
32 #include "berberis/guest_abi/function_wrappers.h"
33 #include "berberis/guest_abi/guest_arguments.h"
34 #include "berberis/guest_abi/guest_params.h"
35 #include "berberis/guest_abi/guest_type.h"
36 #include "berberis/guest_state/guest_addr.h"
37 #include "berberis/guest_state/guest_state.h"
38 #include "berberis/native_bridge/jmethod_shorty.h"
39 #include "berberis/runtime_primitives/host_code.h"
40 #include "berberis/runtime_primitives/runtime_library.h"
41
42 #include "guest_jni_trampolines.h"
43
44 // #define LOG_JNI(...) ALOGE(__VA_ARGS__)
45 #define LOG_JNI(...)
46
47 namespace berberis {
48
49 namespace {
50
ConvertDalvikTypeCharToWrapperTypeChar(char c)51 char ConvertDalvikTypeCharToWrapperTypeChar(char c) {
52 switch (c) {
53 case 'V': // void
54 return 'v';
55 case 'Z': // boolean
56 return 'z';
57 case 'B': // byte
58 return 'b';
59 case 'S': // short
60 return 's';
61 case 'C': // char
62 return 'c';
63 case 'I': // int
64 return 'i';
65 case 'L': // class object - pointer
66 return 'p';
67 case 'J': // long
68 return 'l';
69 case 'F': // float
70 return 'f';
71 case 'D': // double
72 return 'd';
73 default:
74 FATAL("Failed to convert Dalvik char '%c'", c);
75 }
76 }
77
ConvertDalvikShortyToWrapperSignature(char * dst,int size,const char * src,bool add_jnienv_and_jobject)78 void ConvertDalvikShortyToWrapperSignature(char* dst,
79 int size,
80 const char* src,
81 bool add_jnienv_and_jobject) {
82 // return type, env and clazz.
83 CHECK_GT(size, 3);
84 char* cur = dst;
85 *cur++ = ConvertDalvikTypeCharToWrapperTypeChar(*src++);
86
87 if (add_jnienv_and_jobject) {
88 *cur++ = 'p';
89 *cur++ = 'p';
90 }
91
92 while (*src) {
93 CHECK_LT(cur, dst + (size - 1));
94 *cur++ = ConvertDalvikTypeCharToWrapperTypeChar(*src++);
95 }
96
97 *cur = '\0';
98 }
99
RunGuestJNIFunction(GuestAddr pc,GuestArgumentBuffer * buf)100 void RunGuestJNIFunction(GuestAddr pc, GuestArgumentBuffer* buf) {
101 auto [host_jni_env] = HostArgumentsValues<void(JNIEnv*)>(buf);
102 {
103 auto&& [guest_jni_env] = GuestArgumentsReferences<void(JNIEnv*)>(buf);
104 guest_jni_env = ToGuestJNIEnv(host_jni_env);
105 }
106 RunGuestCall(pc, buf);
107 }
108
RunGuestJNIOnLoad(GuestAddr pc,GuestArgumentBuffer * buf)109 void RunGuestJNIOnLoad(GuestAddr pc, GuestArgumentBuffer* buf) {
110 auto [host_java_vm, reserved] = HostArgumentsValues<decltype(JNI_OnLoad)>(buf);
111 {
112 auto&& [guest_java_vm, reserved] = GuestArgumentsReferences<decltype(JNI_OnLoad)>(buf);
113 guest_java_vm = ToGuestJavaVM(host_java_vm);
114 }
115 RunGuestCall(pc, buf);
116 }
117
118 } // namespace
119
WrapGuestJNIFunction(GuestAddr pc,const char * shorty,const char * name,bool has_jnienv_and_jobject)120 HostCode WrapGuestJNIFunction(GuestAddr pc,
121 const char* shorty,
122 const char* name,
123 bool has_jnienv_and_jobject) {
124 const size_t size = strlen(shorty);
125 char signature[size + /* env, clazz and trailing zero */ 3];
126 ConvertDalvikShortyToWrapperSignature(
127 signature, sizeof(signature), shorty, has_jnienv_and_jobject);
128 auto guest_runner = has_jnienv_and_jobject ? RunGuestJNIFunction : RunGuestCall;
129 return WrapGuestFunctionImpl(pc, signature, guest_runner, name);
130 }
131
WrapGuestJNIOnLoad(GuestAddr pc)132 HostCode WrapGuestJNIOnLoad(GuestAddr pc) {
133 return WrapGuestFunctionImpl(pc, "ipp", RunGuestJNIOnLoad, "JNI_OnLoad");
134 }
135
136 namespace {
137
ConvertVAList(JNIEnv * env,jmethodID methodID,GuestVAListParams && params)138 std::vector<jvalue> ConvertVAList(JNIEnv* env, jmethodID methodID, GuestVAListParams&& params) {
139 std::vector<jvalue> result;
140 const char* short_signature = GetJMethodShorty(env, methodID);
141 CHECK(short_signature);
142 short_signature++; // skip return value
143 int len = strlen(short_signature);
144 result.resize(len);
145 for (int i = 0; i < len; i++) {
146 jvalue& arg = result[i];
147 char c = short_signature[i];
148 switch (c) {
149 case 'Z': // boolean (u8)
150 arg.z = params.GetParam<uint8_t>();
151 break;
152 case 'B': // byte (i8)
153 arg.b = params.GetParam<int8_t>();
154 break;
155 case 'S': // short (i16)
156 arg.s = params.GetParam<int16_t>();
157 break;
158 case 'C': // char (u16)
159 arg.c = params.GetParam<uint16_t>();
160 break;
161 case 'I': // int (i32)
162 arg.i = params.GetParam<int32_t>();
163 break;
164 case 'J': // long (i64)
165 arg.j = params.GetParam<int64_t>();
166 break;
167 case 'F': // float - passed as double
168 arg.f = params.GetParam<double>();
169 break;
170 case 'D': // double
171 arg.d = params.GetParam<double>();
172 break;
173 case 'L': // class object (pointer)
174 arg.l = params.GetParam<jobject>();
175 break;
176 default:
177 FATAL("Failed to convert Dalvik char '%c'", c);
178 break;
179 }
180 }
181 return result;
182 }
183
184 // jint RegisterNatives(
185 // JNIEnv *env, jclass clazz,
186 // const JNINativeMethod *methods, jint nMethods);
DoTrampoline_JNIEnv_RegisterNatives(HostCode,ProcessState * state)187 void DoTrampoline_JNIEnv_RegisterNatives(HostCode /* callee */, ProcessState* state) {
188 using PFN_callee = decltype(std::declval<JNIEnv>().functions->RegisterNatives);
189 auto [guest_env, arg_clazz, arg_methods, arg_n] = GuestParamsValues<PFN_callee>(state);
190 JNIEnv* arg_env = ToHostJNIEnv(guest_env);
191
192 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
193 ret = (arg_env->functions)->RegisterNatives(arg_env, arg_clazz, arg_methods, arg_n);
194 }
195
196 // jint GetJavaVM(
197 // JNIEnv *env, JavaVM **vm);
DoTrampoline_JNIEnv_GetJavaVM(HostCode,ProcessState * state)198 void DoTrampoline_JNIEnv_GetJavaVM(HostCode /* callee */, ProcessState* state) {
199 using PFN_callee = decltype(std::declval<JNIEnv>().functions->GetJavaVM);
200 auto [guest_env, arg_vm] = GuestParamsValues<PFN_callee>(state);
201 JNIEnv* arg_env = ToHostJNIEnv(guest_env);
202 JavaVM* host_vm;
203
204 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
205 ret = (arg_env->functions)->GetJavaVM(arg_env, &host_vm);
206 if (ret == 0) {
207 *bit_cast<GuestType<JavaVM*>*>(arg_vm) = ToGuestJavaVM(host_vm);
208 }
209 }
210
DoTrampoline_JNIEnv_CallStaticVoidMethodV(HostCode,ProcessState * state)211 void DoTrampoline_JNIEnv_CallStaticVoidMethodV(HostCode /* callee */, ProcessState* state) {
212 using PFN_callee = decltype(std::declval<JNIEnv>().functions->CallStaticVoidMethodV);
213 auto [arg_env, arg_1, arg_2, arg_va] = GuestParamsValues<PFN_callee>(state);
214 JNIEnv* arg_0 = ToHostJNIEnv(arg_env);
215 std::vector<jvalue> arg_vector = ConvertVAList(arg_0, arg_2, ToGuestAddr(arg_va));
216 jvalue* arg_3 = &arg_vector[0];
217
218 // Note, this call is the only difference from the auto-generated trampoline.
219 JNIEnv_CallStaticVoidMethodV_ForGuest(arg_0, arg_1, arg_2, arg_3);
220
221 (arg_0->functions)->CallStaticVoidMethodA(arg_0, arg_1, arg_2, arg_3);
222 }
223
224 struct KnownMethodTrampoline {
225 unsigned index;
226 TrampolineFunc marshal_and_call;
227 };
228
229 #include "jni_trampolines-inl.h" // NOLINT(build/include)
230
231 // According to our observations there is only one instance of JavaVM
232 // and there are 1 or sometimes more instances of JNIEnv per thread created
233 // by Java Runtime (JNIEnv instances are not shared between different threads).
234 //
235 // This is why we store one global mapping for JavaVM for the app.
236 // And multiple mappings of JNIEnv per thread. There is often only one JNIEnv
237 // per thread, but we have seen examples where 2 instances where created.
238 //
239 // It is likely that the new JNIEnv instance for the thread supersedes the
240 // previous one but the code below does not make this assumption.
241 struct JNIEnvMapping {
242 std::deque<JNIEnv> guest_jni_envs;
243 std::map<GuestType<JNIEnv*>, JNIEnv*> guest_to_host_jni_env;
244 std::map<JNIEnv*, GuestType<JNIEnv*>> host_to_guest_jni_env;
245 };
246
247 std::mutex g_jni_guard_mutex;
248
249 JavaVM g_guest_java_vm;
250 JavaVM* g_host_java_vm;
251
252 // TODO(b/399909631): Add a callback from GuestThread::Destroy to remove entries
253 // from this map.
254 std::map<pid_t, JNIEnvMapping> g_jni_env_mappings;
255
DoJavaVMTrampoline_DestroyJavaVM(HostCode,ProcessState * state)256 void DoJavaVMTrampoline_DestroyJavaVM(HostCode /* callee */, ProcessState* state) {
257 using PFN_callee = decltype(std::declval<JavaVM>().functions->DestroyJavaVM);
258 auto [arg_vm] = GuestParamsValues<PFN_callee>(state);
259 JavaVM* arg_java_vm = ToHostJavaVM(arg_vm);
260
261 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
262 ret = (arg_java_vm->functions)->DestroyJavaVM(arg_java_vm);
263 }
264
265 // jint AttachCurrentThread(JavaVM*, JNIEnv**, void*);
DoJavaVMTrampoline_AttachCurrentThread(HostCode,ProcessState * state)266 void DoJavaVMTrampoline_AttachCurrentThread(HostCode /* callee */, ProcessState* state) {
267 using PFN_callee = decltype(std::declval<JavaVM>().functions->AttachCurrentThread);
268 auto [arg_vm, arg_env_ptr, arg_args] = GuestParamsValues<PFN_callee>(state);
269 JavaVM* arg_java_vm = ToHostJavaVM(arg_vm);
270 JNIEnv* env = nullptr;
271
272 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
273 ret = (arg_java_vm->functions)->AttachCurrentThread(arg_java_vm, &env, arg_args);
274
275 GuestType<JNIEnv*> guest_jni_env = ToGuestJNIEnv(env);
276 memcpy(arg_env_ptr, &guest_jni_env, sizeof(guest_jni_env));
277 }
278
279 // jint DetachCurrentThread(JavaVM*);
DoJavaVMTrampoline_DetachCurrentThread(HostCode,ProcessState * state)280 void DoJavaVMTrampoline_DetachCurrentThread(HostCode /* callee */, ProcessState* state) {
281 using PFN_callee = decltype(std::declval<JavaVM>().functions->DetachCurrentThread);
282 auto [arg_vm] = GuestParamsValues<PFN_callee>(state);
283 JavaVM* arg_java_vm = ToHostJavaVM(arg_vm);
284
285 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
286 ret = (arg_java_vm->functions)->DetachCurrentThread(arg_java_vm);
287 }
288
289 // jint GetEnv(JavaVM*, void**, jint);
DoJavaVMTrampoline_GetEnv(HostCode,ProcessState * state)290 void DoJavaVMTrampoline_GetEnv(HostCode /* callee */, ProcessState* state) {
291 using PFN_callee = decltype(std::declval<JavaVM>().functions->GetEnv);
292 auto [arg_vm, arg_env_ptr, arg_version] = GuestParamsValues<PFN_callee>(state);
293 JavaVM* arg_java_vm = ToHostJavaVM(arg_vm);
294
295 LOG_JNI("JavaVM::GetEnv(%p, %p, %d)", arg_java_vm, arg_env_ptr, arg_version);
296
297 void* env = nullptr;
298 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
299 ret = (arg_java_vm->functions)->GetEnv(arg_java_vm, &env, arg_version);
300
301 GuestType<JNIEnv*> guest_jni_env = ToGuestJNIEnv(static_cast<JNIEnv*>(env));
302 memcpy(arg_env_ptr, &guest_jni_env, sizeof(guest_jni_env));
303
304 LOG_JNI("= jint(%d)", ret);
305 }
306
307 // jint AttachCurrentThreadAsDaemon(JavaVM* vm, void** penv, void* args);
DoJavaVMTrampoline_AttachCurrentThreadAsDaemon(HostCode,ProcessState * state)308 void DoJavaVMTrampoline_AttachCurrentThreadAsDaemon(HostCode /* callee */, ProcessState* state) {
309 using PFN_callee = decltype(std::declval<JavaVM>().functions->AttachCurrentThreadAsDaemon);
310 auto [arg_vm, arg_env_ptr, arg_args] = GuestParamsValues<PFN_callee>(state);
311 JavaVM* arg_java_vm = ToHostJavaVM(arg_vm);
312
313 JNIEnv* env = nullptr;
314 auto&& [ret] = GuestReturnReference<PFN_callee>(state);
315 ret = (arg_java_vm->functions)->AttachCurrentThreadAsDaemon(arg_java_vm, &env, arg_args);
316
317 GuestType<JNIEnv*> guest_jni_env = ToGuestJNIEnv(env);
318 memcpy(arg_env_ptr, &guest_jni_env, sizeof(guest_jni_env));
319 }
320
WrapJavaVM(void * java_vm)321 void WrapJavaVM(void* java_vm) {
322 HostCode* vtable = *reinterpret_cast<HostCode**>(java_vm);
323 // vtable[0] is NULL
324 // vtable[1] is NULL
325 // vtable[2] is NULL
326
327 WrapHostFunctionImpl(vtable[3], DoJavaVMTrampoline_DestroyJavaVM, "JavaVM::DestroyJavaVM");
328
329 WrapHostFunctionImpl(
330 vtable[4], DoJavaVMTrampoline_AttachCurrentThread, "JavaVM::AttachCurrentThread");
331
332 WrapHostFunctionImpl(
333 vtable[5], DoJavaVMTrampoline_DetachCurrentThread, "JavaVM::DetachCurrentThread");
334
335 WrapHostFunctionImpl(vtable[6], DoJavaVMTrampoline_GetEnv, "JavaVM::GetEnv");
336
337 WrapHostFunctionImpl(vtable[7],
338 DoJavaVMTrampoline_AttachCurrentThreadAsDaemon,
339 "JavaVM::AttachCurrentThreadAsDaemon");
340 }
341
342 // We set this to 1 when host JNIEnv/JavaVM functions are wrapped.
343 std::atomic<uint32_t> g_jni_env_wrapped = {0};
344 std::atomic<uint32_t> g_java_vm_wrapped = {0};
345
346 } // namespace
347
ToGuestJNIEnv(JNIEnv * host_jni_env)348 GuestType<JNIEnv*> ToGuestJNIEnv(JNIEnv* host_jni_env) {
349 if (!host_jni_env) {
350 return 0;
351 }
352 // We need to wrap host JNI functions only once. We use an atomic variable
353 // to guard this initialization. Since we use very simple logic without
354 // waiting here, multiple threads can wrap host JNI functions simultaneously.
355 // This is OK since wrapping is thread-safe and later wrappings override
356 // previous ones atomically.
357 // TODO(halyavin) Consider creating a general mechanism for thread-safe
358 // initialization with parameters, if we need it in more than one place.
359 if (std::atomic_load_explicit(&g_jni_env_wrapped, std::memory_order_acquire) == 0U) {
360 WrapJNIEnv(host_jni_env);
361 std::atomic_store_explicit(&g_jni_env_wrapped, 1U, std::memory_order_release);
362 }
363
364 std::lock_guard<std::mutex> lock(g_jni_guard_mutex);
365 pid_t thread_id = GettidSyscall();
366 JNIEnvMapping& mapping = g_jni_env_mappings[thread_id];
367
368 auto it = mapping.host_to_guest_jni_env.find(host_jni_env);
369 if (it != mapping.host_to_guest_jni_env.end()) {
370 return it->second;
371 }
372
373 mapping.guest_jni_envs.emplace_back(*host_jni_env);
374 JNIEnv* guest_jni_env = &mapping.guest_jni_envs.back();
375 auto [unused_it1, host_to_guest_inserted] =
376 mapping.host_to_guest_jni_env.try_emplace(host_jni_env, guest_jni_env);
377 CHECK(host_to_guest_inserted);
378
379 auto [unused_it2, guest_to_host_inserted] =
380 mapping.guest_to_host_jni_env.try_emplace(guest_jni_env, host_jni_env);
381 CHECK(guest_to_host_inserted);
382
383 return guest_jni_env;
384 }
385
ToHostJNIEnv(GuestType<JNIEnv * > guest_jni_env)386 JNIEnv* ToHostJNIEnv(GuestType<JNIEnv*> guest_jni_env) {
387 std::lock_guard<std::mutex> lock(g_jni_guard_mutex);
388 pid_t thread_id = GettidSyscall();
389 JNIEnvMapping& mapping = g_jni_env_mappings[thread_id];
390
391 auto it = mapping.guest_to_host_jni_env.find(guest_jni_env);
392
393 if (it == mapping.guest_to_host_jni_env.end()) {
394 ALOGE("Unexpected guest JNIEnv: %p (it was never passed to guest), passing to host 'as is'",
395 ToHostAddr(guest_jni_env));
396 TRACE("Unexpected guest JNIEnv: %p (it was never passed to guest), passing to host 'as is'",
397 ToHostAddr(guest_jni_env));
398 return ToHostAddr(guest_jni_env);
399 }
400
401 return it->second;
402 }
403
ToGuestJavaVM(JavaVM * host_java_vm)404 GuestType<JavaVM*> ToGuestJavaVM(JavaVM* host_java_vm) {
405 CHECK(host_java_vm);
406 if (std::atomic_load_explicit(&g_java_vm_wrapped, std::memory_order_acquire) == 0U) {
407 WrapJavaVM(host_java_vm);
408 std::atomic_store_explicit(&g_java_vm_wrapped, 1U, std::memory_order_release);
409 }
410
411 std::lock_guard<std::mutex> lock(g_jni_guard_mutex);
412 if (g_host_java_vm == nullptr) {
413 g_guest_java_vm = *host_java_vm;
414 g_host_java_vm = host_java_vm;
415 }
416
417 if (g_host_java_vm != host_java_vm) {
418 TRACE("Warning: Unexpected host JavaVM: %p (expecting %p), passing as is",
419 host_java_vm,
420 g_host_java_vm);
421 return host_java_vm;
422 }
423
424 return &g_guest_java_vm;
425 }
426
ToHostJavaVM(GuestType<JavaVM * > guest_java_vm)427 JavaVM* ToHostJavaVM(GuestType<JavaVM*> guest_java_vm) {
428 std::lock_guard<std::mutex> lock(g_jni_guard_mutex);
429 if (ToHostAddr(guest_java_vm) == &g_guest_java_vm) {
430 return g_host_java_vm;
431 }
432
433 TRACE("Warning: Unexpected guest JavaVM: %p (expecting %p), passing as is",
434 ToHostAddr(guest_java_vm),
435 &g_guest_java_vm);
436
437 return ToHostAddr(guest_java_vm);
438 }
439
440 } // namespace berberis
441