• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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