Fix JNI ref leaks

This commit is contained in:
solohsu 2019-06-13 22:48:37 +08:00
parent 3d863cb4c2
commit c4978b7344
8 changed files with 83 additions and 34 deletions

View File

@ -123,6 +123,11 @@ public class Main implements KeepAll {
return edxpImplRef.get();
}
@EdxpImpl.Variant
public static synchronized int getEdxpVariant() {
return getEdxpImpl().getVariant();
}
private static void loadEdxpImpls() {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
@ -133,7 +138,7 @@ public class Main implements KeepAll {
iterator.next();
}
} catch (Throwable t) {
// Do nothing
Utils.logE("error when loadEdxpImpls", t);
}
return null;
}

View File

@ -3,10 +3,10 @@ import org.gradle.internal.os.OperatingSystem
apply plugin: 'com.android.library'
version "v0.4.4.0_alpha"
version "v0.4.4.1_alpha"
ext {
versionCode = "4400"
versionCode = "4410"
module_name = "EdXposed"
jar_dest_dir = "${projectDir}/template_override/system/framework/"
is_windows = OperatingSystem.current().isWindows()

View File

@ -85,7 +85,11 @@ ALWAYS_INLINE static int ClearException(JNIEnv *env) {
#define JNI_CallStaticObjectMethod(env, obj, ...) \
env->CallStaticObjectMethod(obj, __VA_ARGS__); \
if (ClearException(env)) LOGE("CallStaticVoidMethod " #obj " " #__VA_ARGS__);
if (ClearException(env)) LOGE("CallStaticObjectMethod " #obj " " #__VA_ARGS__);
#define JNI_CallStaticIntMethod(env, obj, ...) \
env->CallStaticIntMethod(obj, __VA_ARGS__); \
if (ClearException(env)) LOGE("CallStaticIntMethod " #obj " " #__VA_ARGS__);
#define JNI_GetArrayLength(env, array) \
env->GetArrayLength(array); \

View File

@ -13,16 +13,25 @@ namespace art {
return NewLocalRefSym(env, mirror_ptr);
}
CREATE_FUNC_SYMBOL_ENTRY(void, DeleteLocalRef, void *env, jobject obj) {
DeleteLocalRefSym(env, obj);
}
public:
JNIEnvExt(void *thiz) : HookedObject(thiz) {}
static void Setup(void *handle, HookFunType hook_func) {
RETRIEVE_FUNC_SYMBOL(NewLocalRef, "_ZN3art9JNIEnvExt11NewLocalRefEPNS_6mirror6ObjectE");
RETRIEVE_FUNC_SYMBOL(DeleteLocalRef, "_ZN3art9JNIEnvExt14DeleteLocalRefEP8_jobject");
}
jobject NewLocalRefer(void *mirror_ptr) {
return NewLocalRef(thiz_, mirror_ptr);
}
void DeleteLocalRef(jobject obj) {
DeleteLocalRef(thiz_, obj);
}
};

View File

@ -33,13 +33,14 @@ namespace edxp {
}
void Context::CallOnPostFixupStaticTrampolines(void *class_ptr) {
if (post_fixup_static_mid_ != nullptr) {
JNIEnv *env;
vm_->GetEnv((void **) (&env), JNI_VERSION_1_4);
art::JNIEnvExt env_ext(env);
jobject clazz = env_ext.NewLocalRefer(class_ptr);
JNI_CallStaticVoidMethod(env, class_linker_class_, post_fixup_static_mid_, clazz);
if (UNLIKELY(!post_fixup_static_mid_ || !class_linker_class_)) {
return;
}
JNIEnv *env;
vm_->GetEnv((void **) (&env), JNI_VERSION_1_4);
art::JNIEnvExt env_ext(env);
ScopedLocalRef clazz(env, env_ext.NewLocalRefer(class_ptr));
JNI_CallStaticVoidMethod(env, class_linker_class_, post_fixup_static_mid_, clazz.get());
}
void Context::LoadDexAndInit(JNIEnv *env, const char *dex_path) {
@ -60,12 +61,22 @@ namespace edxp {
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)V");
jobject my_cl = JNI_NewObject(env, path_classloader, initMid, env->NewStringUTF(dex_path),
nullptr, sys_classloader);
if (UNLIKELY(!my_cl)) {
env->DeleteLocalRef(classloader);
env->DeleteLocalRef(sys_classloader);
env->DeleteLocalRef(path_classloader);
if (UNLIKELY(my_cl == nullptr)) {
LOG(ERROR) << "PathClassLoader creation failed!!!";
return;
}
// TODO clear up all these global refs if blacklisted?
inject_class_loader_ = env->NewGlobalRef(my_cl);
env->DeleteLocalRef(my_cl);
// initialize pending methods related
env->GetJavaVM(&vm_);
class_linker_class_ = (jclass) env->NewGlobalRef(
@ -84,22 +95,36 @@ namespace edxp {
RegisterArtHeap(env);
RegisterEdxpYahfa(env);
// must call entry class's methods after all native methods registered
if (LIKELY(entry_class_)) {
jmethodID get_variant_mid = JNI_GetStaticMethodID(env, entry_class_,
"getEdxpVariant", "()I");
if (LIKELY(get_variant_mid)) {
int variant = JNI_CallStaticIntMethod(env, entry_class_, get_variant_mid);
variant_ = static_cast<Variant>(variant);
}
}
initialized_ = true;
//for SandHook variant
ScopedDlHandle sandhook_handle(kLibSandHookPath.c_str());
if (!sandhook_handle.IsValid()) {
return;
}
typedef bool *(*TYPE_JNI_LOAD)(JNIEnv *, jclass, jclass);
auto jni_load = sandhook_handle.DlSym<TYPE_JNI_LOAD>("JNI_Load_Ex");
jclass sandhook_class = FindClassFromLoader(env, kSandHookClassName);
jclass nevercall_class = FindClassFromLoader(env, kSandHookNeverCallClassName);
if (!sandhook_class || !nevercall_class) { // fail-fast
return;
}
if (!jni_load(env, sandhook_class, nevercall_class)) {
LOGE("SandHook: HookEntry class error. %d", getpid());
if (variant_ == SANDHOOK) {
//for SandHook variant
ScopedDlHandle sandhook_handle(kLibSandHookPath.c_str());
if (!sandhook_handle.IsValid()) {
return;
}
typedef bool *(*TYPE_JNI_LOAD)(JNIEnv *, jclass, jclass);
auto jni_load = sandhook_handle.DlSym<TYPE_JNI_LOAD>("JNI_Load_Ex");
ScopedLocalRef sandhook_class(env, FindClassFromLoader(env, kSandHookClassName));
ScopedLocalRef nevercall_class(env,
FindClassFromLoader(env, kSandHookNeverCallClassName));
if (sandhook_class == nullptr || nevercall_class == nullptr) { // fail-fast
return;
}
if (!jni_load(env, sandhook_class.get(), nevercall_class.get())) {
LOGE("SandHook: HookEntry class error. %d", getpid());
}
}
}
@ -136,7 +161,7 @@ namespace edxp {
inline void Context::FindAndCall(JNIEnv *env, const char *method_name,
const char *method_sig, ...) const {
if (!entry_class_) {
if (UNLIKELY(!entry_class_)) {
LOGE("cannot call method %s, entry class is null", method_name);
return;
}
@ -235,8 +260,7 @@ namespace edxp {
if (res == 0) {
PrepareJavaEnv(env);
FindAndCall(env, "forkAndSpecializePost", "(ILjava/lang/String;Ljava/lang/String;)V",
res,
app_data_dir_, nice_name_);
res, app_data_dir_, nice_name_);
} else {
// in zygote process, res is child zygote pid
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66

View File

@ -8,6 +8,13 @@ namespace edxp {
#define SYSTEM_SERVER_DATA_DIR "/data/user/0/android"
enum Variant {
NONE = 0,
YAHFA = 1,
SANDHOOK = 2,
WHALE = 3
};
class Context {
public:
@ -52,6 +59,7 @@ namespace edxp {
private:
static Context *instance_;
bool initialized_ = false;
Variant variant_ = NONE;
jobject inject_class_loader_ = nullptr;
jclass entry_class_ = nullptr;
jstring app_data_dir_ = nullptr;

View File

@ -62,13 +62,12 @@ namespace edxp {
hook_func = reinterpret_cast<HookFunType>(hook_func_symbol);
if (api_level >= ANDROID_P) {
void *handle = DlOpen(kLibDlPath.c_str());
ScopedDlHandle dl_handle(kLibDlPath.c_str());
void *handle = dl_handle.Get();
HOOK_FUNC(mydlopen, "__loader_dlopen");
dlclose(handle);
} else {
void *art_handle = DlOpen(kLibArtPath.c_str());
InstallArtHooks(art_handle);
dlclose(art_handle);
ScopedDlHandle art_handle(kLibArtPath.c_str());
InstallArtHooks(art_handle.Get());
}
}

View File

@ -1,6 +1,6 @@
#!/system/bin/sh
EDXP_VERSION="0.4.4.0_alpha (4400)"
EDXP_VERSION="0.4.4.1_alpha (4410)"
ANDROID_SDK=`getprop ro.build.version.sdk`
BUILD_DESC=`getprop ro.build.description`
PRODUCT=`getprop ro.build.product`