Skip to content

Commit e15d9aa

Browse files
committed
Fix JNI stale reference error in getSeed method
- Extract seedOffset string content BEFORE acquiring mutex lock - Prevent JNI reference invalidation by using std::string copy in critical section - Add explicit scope boundaries for mutex-protected code - Improve error handling with null checks after GetStringUTFChars - Resolves 'attempt to use stale Global' crash by ensuring no JNI calls while mutex is held Fixes issue where seedOffset jstring reference became stale when used across mutex boundary.
1 parent 4109bbb commit e15d9aa

File tree

1 file changed

+51
-65
lines changed

1 file changed

+51
-65
lines changed

app/src/main/cpp/monerujo.cpp

Lines changed: 51 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -716,104 +716,90 @@ Java_com_m2049r_xmrwallet_model_Wallet_getSeed(
716716
JNIEnv *env,
717717
jobject instance,
718718
jstring seedOffset) {
719-
719+
720720
LOGD("getSeed: ENTER - thread: %ld", (long)gettid());
721-
721+
722722
// ============================================================
723-
// PHASE 1: ALL JNI OPERATIONS - NO MUTEX YET
723+
// PHASE 1: ALL JNI OPERATIONS BEFORE MUTEX
724724
// ============================================================
725725

726-
// Safety check: ensure env is valid
727726
if (env == nullptr) {
728727
LOGE("getSeed: ERROR - JNIEnv is null");
729728
return nullptr;
730729
}
731-
732-
// Get wallet handle BEFORE mutex (read-only operation, safe)
733-
Monero::Wallet *wallet = nullptr;
734-
try {
735-
wallet = getHandle<Monero::Wallet>(env, instance);
736-
} catch (...) {
737-
LOGE("getSeed: EXCEPTION getting wallet handle");
738-
return env->NewStringUTF("");
739-
}
740-
730+
731+
// Get wallet handle
732+
Monero::Wallet *wallet = getHandle<Monero::Wallet>(env, instance);
741733
if (!wallet) {
742734
LOGE("getSeed: ERROR - Wallet handle is null");
743735
return env->NewStringUTF("");
744736
}
745-
746-
// Extract seedOffset string IMMEDIATELY - this is the critical fix
747-
// We MUST do this BEFORE acquiring any mutex
737+
738+
// CRITICAL: Extract string content BEFORE mutex
748739
std::string offset;
749740
if (seedOffset != nullptr) {
750741
const char *off = env->GetStringUTFChars(seedOffset, nullptr);
751-
752-
// Check for JNI exceptions
753-
if (env->ExceptionCheck()) {
754-
env->ExceptionClear();
755-
LOGE("getSeed: JNI exception while reading seedOffset");
742+
if (off == nullptr || env->ExceptionCheck()) {
743+
if (env->ExceptionCheck()) {
744+
env->ExceptionClear();
745+
}
746+
LOGE("getSeed: Failed to get UTF chars from seedOffset");
756747
return env->NewStringUTF("");
757748
}
758-
759-
if (off != nullptr) {
760-
offset.assign(off);
761-
env->ReleaseStringUTFChars(seedOffset, off);
762-
}
749+
750+
offset.assign(off);
751+
env->ReleaseStringUTFChars(seedOffset, off);
752+
// seedOffset jstring is now "dead" to us - never touch it again
763753
}
764754

765-
// IMPORTANT: seedOffset jstring is NEVER touched again after this point
766-
// We only use the std::string copy (offset) from now on
767-
768755
// ============================================================
769-
// PHASE 2: CRITICAL SECTION - MUTEX HELD, NO JNI CALLS
756+
// PHASE 2: CRITICAL SECTION - NO JNI CALLS WHILE LOCKED
770757
// ============================================================
771758

772-
// NOW acquire the mutex (after ALL JNI operations are complete)
773-
std::lock_guard<std::mutex> lock(g_walletMutex);
774-
LOGD("getSeed: acquired wallet mutex");
775-
776-
// Wallet status check
777-
if (wallet->status() != Monero::Wallet::Status_Ok) {
778-
std::string err;
779-
int status;
780-
wallet->statusWithErrorString(status, err);
781-
LOGE("getSeed: Wallet status not OK (%d): %s", status, err.c_str());
782-
// Return empty string on error
783-
// Note: Returning while holding lock is fine - lock_guard destructor releases it
784-
return env->NewStringUTF("");
785-
}
786-
787-
// Get seed using the C++ string copy (NOT the jstring parameter)
788759
std::string seed;
789-
try {
790-
seed = wallet->seed(offset);
791-
} catch (const std::exception& e) {
792-
LOGE("getSeed: EXCEPTION in wallet->seed: %s", e.what());
793-
return env->NewStringUTF("");
794-
} catch (...) {
795-
LOGE("getSeed: UNKNOWN EXCEPTION in wallet->seed");
796-
return env->NewStringUTF("");
797-
}
798-
799-
// Lock is automatically released here when lock_guard goes out of scope
760+
{
761+
std::lock_guard<std::mutex> lock(g_walletMutex);
762+
LOGD("getSeed: acquired wallet mutex");
763+
764+
// Wallet status check
765+
if (wallet->status() != Monero::Wallet::Status_Ok) {
766+
std::string err;
767+
int status;
768+
wallet->statusWithErrorString(status, err);
769+
LOGE("getSeed: Wallet status not OK (%d): %s", status, err.c_str());
770+
// Lock releases here when scope ends
771+
return env->NewStringUTF("");
772+
}
773+
774+
// Get seed using C++ string (safe - no JNI involved)
775+
try {
776+
seed = wallet->seed(offset);
777+
} catch (const std::exception& e) {
778+
LOGE("getSeed: EXCEPTION in wallet->seed: %s", e.what());
779+
return env->NewStringUTF("");
780+
} catch (...) {
781+
LOGE("getSeed: UNKNOWN EXCEPTION in wallet->seed");
782+
return env->NewStringUTF("");
783+
}
784+
} // Lock automatically released here
800785

801786
// ============================================================
802-
// PHASE 3: CREATE RETURN VALUE - MUTEX RELEASED, JNI SAFE
787+
// PHASE 3: CREATE RETURN VALUE - MUTEX RELEASED
803788
// ============================================================
804789

805-
// Create Java string from the result
806790
jstring result = env->NewStringUTF(seed.c_str());
807-
808-
if (env->ExceptionCheck() || result == nullptr) {
809-
env->ExceptionClear();
810-
LOGE("getSeed: Failed to create jstring");
791+
if (result == nullptr || env->ExceptionCheck()) {
792+
if (env->ExceptionCheck()) {
793+
env->ExceptionClear();
794+
}
795+
LOGE("getSeed: Failed to create return jstring");
811796
return env->NewStringUTF("");
812797
}
813-
798+
814799
LOGD("getSeed: EXIT - success");
815800
return result;
816801
}
802+
817803
JNIEXPORT jstring JNICALL
818804
Java_com_m2049r_xmrwallet_model_Wallet_getSeedLanguage(
819805
JNIEnv *env,

0 commit comments

Comments
 (0)