Require mutator lock for DeleteLocalRef
There was a race condition where suspended threads could call
DeleteLocalRef while the GC was marking their roots. This could
cause the GC to attempt to mark a null object.
Bug: 22119403
Change-Id: I962c717bb87b2acb2a4710a2d7ab16793e031401
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index cb36183..cc176b7 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -554,15 +554,16 @@
return soa.AddLocalReference<jobject>(decoded_obj);
}
- static void DeleteLocalRef(JNIEnv* env, jobject obj)
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ static void DeleteLocalRef(JNIEnv* env, jobject obj) {
if (obj == nullptr) {
return;
}
- IndirectReferenceTable& locals = reinterpret_cast<JNIEnvExt*>(env)->locals;
-
- uint32_t cookie = reinterpret_cast<JNIEnvExt*>(env)->local_ref_cookie;
- if (!locals.Remove(cookie, obj)) {
+ // SOA is only necessary to have exclusion between GC root marking and removing.
+ // We don't want to have the GC attempt to mark a null root if we just removed
+ // it. b/22119403
+ ScopedObjectAccess soa(env);
+ auto* ext_env = down_cast<JNIEnvExt*>(env);
+ if (!ext_env->locals.Remove(ext_env->local_ref_cookie, obj)) {
// Attempting to delete a local reference that is not in the
// topmost local reference frame is a no-op. DeleteLocalRef returns
// void and doesn't throw any exceptions, but we should probably