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