microsoft / microsoft/msix-packaging
[BUG] AOSP Jni singleton caches JNIEnv* and can reuse it on the wrong thread
Nobody has claimed this yet.
- Dominant language
- C
- Stars
- 1.1k
- Forks
- 193
- Avg merge
- 2h 16m
- Merged PRs (30d)
- 2
Description
Project
MSIX SDK
Describe the bug
Jni is a process-wide Meyers singleton (Jni::Instance()). GetEnv() stores JNIEnv* in m_env and, after the first successful lookup, returns that pointer forever.
JNIEnv* is valid only for the thread that obtained it. A later call on another thread skips JavaVM::GetEnv / AttachCurrentThread and uses the first thread’s env. JNI calls and DeleteLocalRef (via JObjectDeleter) then run against the wrong environment.
The destructor calls DetachCurrentThread if m_isThreadAttached is set, but the static singleton is destroyed at process exit on whatever thread runs atexit, which may not be the thread that attached.
To Reproduce
From source inspection of current master; no runtime log attached.
- Build the AOSP (
-DAOSP=on) configuration. - First JNI use happens on thread A:
Jni::Instance()->GetEnv()cachesm_env. - Thread B later calls
Jni::Instance()->GetEnv()or destroys aunique_ptrthat usesJObjectDeleter. - Thread B receives thread A’s
JNIEnv*without aGetEnv/Attachon B.
https://github.com/microsoft/msix-packaging/blob/master/src/msix/PAL/Interop/AOSP/JniHelper.hpp
static Jni* Instance()
{
static Jni jni;
return &jni;
}
JNIEnv* GetEnv()
{
if (!m_env)
{
int result = g_JavaVM->GetEnv(reinterpret_cast<void**>(&m_env), JNI_VERSION_1_6);
if (result < 0)
{
result = g_JavaVM->AttachCurrentThread(&m_env, nullptr);
m_isThreadAttached = true;
}
}
return m_env;
}
Expected behavior
Do not cache JNIEnv* on the singleton. Each GetEnv() should call JavaVM::GetEnv for the current thread, and AttachCurrentThread only when that thread is detached. DetachCurrentThread must run on the same thread that attached (for example thread_local with a destructor), not in the process-wide ~Jni().
Screenshots
N/A
Platform
Android / AOSP (src/msix/PAL/Interop/AOSP/JniHelper.hpp, current master)
Additional context
JObjectDeleter always does Jni::Instance()->GetEnv()->DeleteLocalRef(obj), so a local ref created on one thread can be deleted with another thread’s env if the unique_ptr is released there.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/msix/PAL/Interop/AOSP/JniHelper.hpp and inspect Jni::GetEnv(), the singleton lifetime, and JObjectDeleter. Build the AOSP configuration, then verify that each call resolves the current thread's JNIEnv* and that thread attachment is detached on the same thread. Done means no process-wide JNIEnv* cache or destructor-time detach remains.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, cpp
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100