Race Condition in initializeEncoding() function in jni_util.c

Ronald Servant Ronald_Servant at ca.ibm.com
Thu Nov 10 15:01:48 UTC 2016



Hi,

While building the J9 JVM for the IBM SDK for Java(*), we've run across
what we believe is a race condition in initializeEncoding(JNIEnv*) in
jni_util.c.

      (*)  The IBM SDK for Java is comprised of the J9 JVM and OpenJDK
class library.
We've encountered a situation where (*env)->CallObjectMethod() in
JNU_GetStringPlatformChars() is invoked with an uninitialized method ID
(the static variable "String_getBytes_ID").

The function initializeEnconding() initializes 4 static variables, but only
one of them ("fastEncoding") is checked to see if initializeEnconding()
should be called in JNU_NewStringPlatform() and JNU_GetStringPlatformChars
().  Further, "fastEncoding" is the first of the 4 static variables set by
initializeEncoding() leaving a race condition where another thread sees
"fastEncoding" set but "String_getBytes_ID" is not yet initialized.

I believe that moving the initialization of "String_getBytes_ID" and
"String_init_ID" to the top of the function would eliminate the impact of
the race condition.

I'd also recommend moving the initialization of "jnuEncoding" up one line,
but this may lead to leaking a global ref.

We are able to reproduce the failure in JNU_GetStringPlatformChars() due to
using an null String_getBytes_ID variable for a method id race condition in
our environment 1 in 20.  With the following patch applied, the race
condition can no longer be reproduced.

Do you agree there is a race condition?  If so, would the patch be an
appropriate fix?  We could introduce locking, but that feels like
overkill.

Ron.

diff -r efa71dc820eb src/java.base/share/native/libjava/jni_util.c
--- a/src/java.base/share/native/libjava/jni_util.c    Mon Nov 07 13:10:42
2016 -0400
+++ b/src/java.base/share/native/libjava/jni_util.c    Wed Nov 09 17:19:16
2016 -0500
@@ -688,6 +688,15 @@

     strClazz = JNU_ClassString(env);
     CHECK_NULL(strClazz);
+
+    /* Initialize method-id cache */
+    String_getBytes_ID = (*env)->GetMethodID(env, strClazz,
+                                             "getBytes",
"(Ljava/lang/String;)[B");
+    CHECK_NULL(String_getBytes_ID);
+    String_init_ID = (*env)->GetMethodID(env, strClazz,
+                                         "<init>",
"([BLjava/lang/String;)V");
+    CHECK_NULL(String_init_ID);
+

     propname = (*env)->NewStringUTF(env, "sun.jnu.encoding");
     if (propname) {
@@ -727,8 +736,8 @@
                              strcmp(encname, "utf-16le") == 0)
                         fastEncoding = FAST_CP1252;
                     else {
+                        jnuEncoding = (jstring)(*env)->NewGlobalRef(env,
enc);
                         fastEncoding = NO_FAST_ENCODING;
-                        jnuEncoding = (jstring)(*env)->NewGlobalRef(env,
enc);
                     }
                     (*env)->ReleaseStringUTFChars(env, enc, encname);
                 }
@@ -741,13 +750,6 @@
     }
     (*env)->DeleteLocalRef(env, propname);
     (*env)->DeleteLocalRef(env, enc);
-
-    /* Initialize method-id cache */
-    String_getBytes_ID = (*env)->GetMethodID(env, strClazz,
-                                             "getBytes",
"(Ljava/lang/String;)[B");
-    CHECK_NULL(String_getBytes_ID);
-    String_init_ID = (*env)->GetMethodID(env, strClazz,
-                                         "<init>",
"([BLjava/lang/String;)V");
 }





More information about the core-libs-dev mailing list