Fix race by lock-free backup implementation

This commit is contained in:
LoveSy 2023-06-25 16:07:54 +08:00
parent 325e1e9b15
commit 40845b3f74
3 changed files with 26 additions and 28 deletions

View File

@ -1,6 +1,5 @@
package org.lsposed.lspd.nativebridge; package org.lsposed.lspd.nativebridge;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable; import java.lang.reflect.Executable;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;

View File

@ -31,12 +31,29 @@ using namespace lsplant;
namespace { namespace {
struct HookItem { struct HookItem {
jobject backup {nullptr};
std::multimap<jint, jobject, std::greater<>> callbacks {}; std::multimap<jint, jobject, std::greater<>> callbacks {};
private:
std::atomic<jobject> backup {nullptr};
static_assert(decltype(backup)::is_always_lock_free);
inline static jobject FAILED = reinterpret_cast<jobject>(std::numeric_limits<uintptr_t>::max());
public:
jobject GetBackup() {
backup.wait(nullptr, std::memory_order::acquire);
if (auto bk = backup.load(std::memory_order_relaxed); bk != FAILED) {
return bk;
} else {
return nullptr;
}
}
void SetBackup(jobject newBackup) {
jobject null = nullptr;
backup.compare_exchange_strong(null, newBackup ? newBackup : FAILED,
std::memory_order_acq_rel, std::memory_order_relaxed);
backup.notify_all();
}
}; };
std::shared_mutex hooked_lock; std::shared_mutex hooked_lock;
std::recursive_mutex backup_lock;
absl::flat_hash_map<jmethodID, std::unique_ptr<HookItem>> hooked_methods; absl::flat_hash_map<jmethodID, std::unique_ptr<HookItem>> hooked_methods;
jmethodID invoke = nullptr; jmethodID invoke = nullptr;
@ -85,15 +102,10 @@ LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, hookMethod, jobject hookMethod,
"([Ljava/lang/Object;)Ljava/lang/Object;"), "([Ljava/lang/Object;)Ljava/lang/Object;"),
false); false);
auto hooker_object = env->NewObject(hooker, init, hookMethod); auto hooker_object = env->NewObject(hooker, init, hookMethod);
std::unique_lock lk(backup_lock); hook_item->SetBackup(lsplant::Hook(env, hookMethod, hooker_object, callback_method));
hook_item->backup = lsplant::Hook(env, hookMethod, hooker_object, callback_method);
env->DeleteLocalRef(hooker_object); env->DeleteLocalRef(hooker_object);
} }
jobject backup = nullptr; jobject backup = hook_item->GetBackup();
{
std::unique_lock lk(backup_lock);
backup = hook_item->backup;
}
if (!backup) return JNI_FALSE; if (!backup) return JNI_FALSE;
JNIMonitor monitor(env, backup); JNIMonitor monitor(env, backup);
hook_item->callbacks.emplace(std::make_pair(priority, env->NewGlobalRef(callback))); hook_item->callbacks.emplace(std::make_pair(priority, env->NewGlobalRef(callback)));
@ -110,11 +122,7 @@ LSP_DEF_NATIVE_METHOD(jboolean, HookBridge, unhookMethod, jobject hookMethod, jo
} }
} }
if (!hook_item) return JNI_FALSE; if (!hook_item) return JNI_FALSE;
jobject backup = nullptr; jobject backup = hook_item->GetBackup();
{
std::unique_lock lk(backup_lock);
backup = hook_item->backup;
}
if (!backup) return JNI_FALSE; if (!backup) return JNI_FALSE;
JNIMonitor monitor(env, backup); JNIMonitor monitor(env, backup);
for (auto i = hook_item->callbacks.begin(); i != hook_item->callbacks.end(); ++i) { for (auto i = hook_item->callbacks.begin(); i != hook_item->callbacks.end(); ++i) {
@ -141,12 +149,7 @@ LSP_DEF_NATIVE_METHOD(jobject, HookBridge, invokeOriginalMethod, jobject hookMet
hook_item = found->second.get(); hook_item = found->second.get();
} }
} }
jobject to_call = hookMethod; return env->CallObjectMethod(hook_item ? hook_item->GetBackup() : hookMethod, invoke, thiz, args);
if (hook_item) {
std::unique_lock lk(backup_lock);
if (hook_item->backup) to_call = hook_item->backup;
}
return env->CallObjectMethod(to_call, invoke, thiz, args);
} }
LSP_DEF_NATIVE_METHOD(jobject, HookBridge, allocateObject, jclass cls) { LSP_DEF_NATIVE_METHOD(jobject, HookBridge, allocateObject, jclass cls) {
@ -185,7 +188,7 @@ LSP_DEF_NATIVE_METHOD(jobject, HookBridge, invokeSpecialMethod, jobject method,
std::vector<jvalue> a(param_len); std::vector<jvalue> a(param_len);
auto *const shorty_char = env->GetCharArrayElements(shorty, nullptr); auto *const shorty_char = env->GetCharArrayElements(shorty, nullptr);
for (jint i = 0; i != param_len; ++i) { for (jint i = 0; i != param_len; ++i) {
jobject element = nullptr; jobject element;
switch(shorty_char[i + 1]) { switch(shorty_char[i + 1]) {
case 'I': case 'I':
a[i].i = env->CallIntMethod(element = env->GetObjectArrayElement(args, i), get_int); a[i].i = env->CallIntMethod(element = env->GetObjectArrayElement(args, i), get_int);
@ -276,11 +279,7 @@ LSP_DEF_NATIVE_METHOD(jobjectArray, HookBridge, callbackSnapshot, jobject method
} }
} }
if (!hook_item) return nullptr; if (!hook_item) return nullptr;
jobject backup = nullptr; jobject backup = hook_item->GetBackup();
{
std::unique_lock lk(backup_lock);
backup = hook_item->backup;
}
if (!backup) return nullptr; if (!backup) return nullptr;
JNIMonitor monitor(env, backup); JNIMonitor monitor(env, backup);
auto res = env->NewObjectArray((jsize) hook_item->callbacks.size(), env->FindClass("java/lang/Object"), nullptr); auto res = env->NewObjectArray((jsize) hook_item->callbacks.size(), env->FindClass("java/lang/Object"), nullptr);

2
external/lsplant vendored

@ -1 +1 @@
Subproject commit c4b670709bf42e209e77e2a29ada15f0e37db239 Subproject commit 03517ca86d6517c0e468055240e3c2e115f76180