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