Possible atomicity violations when composing concurrent collections operations

Yu Lin yu.lin.86 at gmail.com
Fri Aug 3 15:05:41 PDT 2012


Hi,

My name is Yu Lin. I'm a Ph.D. student in the CS department at
UIUC. I'm currently doing research on mining Java concurrent library
misuses. I found some uses of concurrent collections in OpenJDK8 may
result in potential atomicity violation bugs. I just posted this issue
to the mailing list of OpenJdk7u but I was suggested to post here because
OpenJDK8 is more active recently.

The code below is a snapshot of the code in file
jdk/src/share/classes/java/security/Security.java
from line 669 to 681

L669   private static Class getSpiClass(String type) {
L670        Class clazz = spiMap.get(type);
L671        if (clazz != null) {
L672            return clazz;
L673        }
L674        try {
L675            clazz = Class.forName("java.security." + type + "Spi");
L676            spiMap.put(type, clazz);
L677            return clazz;
L678        }
            ...
L681   }

In the code above, there might be an atomicity violation: suppose a
thread T1 executes line 670 and finds out the concurrent hashmap
"spiMap" does not contain the key "type". Before it gets to execute
line 676, another thread T2 puts a pair <type, v> in the map
"spiMap". Now thread T1 resumes execution and it will overwrite the
value written by thread T2. Thus, the code no longer preserves the
"put-if-absent" semantics, and thread T2 will return a "clazz" object
that is not in the map at line 677. If such interleaving may happen,
it may result in buggy behavior. I assume that it may happen because
the concurrent hashmap is usually used in a multithreaded
context. This violation can be avoided by using "putIfAbsent" method.

There are many other places in Helix that have the same code patterns
as the above two examples, but I'm not sure which will lead to bug and
which not. If you think this kind of problem is important and may lead
to buggy behavior, I can list all of them.

Moreover, there are other two kinds of potential bugs:
in jdk/src/share/classes/java/lang/Thread.java from line 16662 to 1668,

L1662   Boolean result = Caches.subclassAudits.get(key);
L1663   if (result == null) {
L1664       result = Boolean.valueOf(auditSubclass(cl));
L1665       Caches.subclassAudits.putIfAbsent(key, result);
L1666   }

L1668   return result.booleanValue();

If thread T2 puts a pair <key, result2> after thread T1 finds "key"
isn't in the map but just before T1 executes line 1665, the <key,
result> in T1 won't be put. So T1 returns a "result" which is not in
the map. Actually, the return value of "putIfAbsent" method should be
checked here.

I append a patch to show the fix of the above problems. I can also list
all of such code I found if you need.

Regards,
Yu

==================================================

diff -r 79c9847d4a5f src/share/classes/java/lang/Thread.java
--- a/src/share/classes/java/lang/Thread.java   Thu Aug 02 15:36:39 2012 -0700
+++ b/src/share/classes/java/lang/Thread.java   Fri Aug 03 11:14:13 2012 -0500
@@ -1662,7 +1662,9 @@
         Boolean result = Caches.subclassAudits.get(key);
         if (result == null) {
             result = Boolean.valueOf(auditSubclass(cl));
-            Caches.subclassAudits.putIfAbsent(key, result);
+            Boolean tmpResult = Caches.subclassAudits.putIfAbsent(key, result);
+            if(tmpResult != null)
+                result = tmpResult;
         }

         return result.booleanValue();
diff -r 79c9847d4a5f src/share/classes/java/security/Security.java
--- a/src/share/classes/java/security/Security.java     Thu Aug 02
15:36:39 2012 -0700
+++ b/src/share/classes/java/security/Security.java     Fri Aug 03
11:14:13 2012 -0500
@@ -658,7 +658,7 @@
     }

     // Map containing cached Spi Class objects of the specified type
-    private static final Map<String, Class<?>> spiMap =
+    private static final ConcurrentHashMap<String, Class<?>> spiMap =
             new ConcurrentHashMap<>();

     /**
@@ -673,7 +673,9 @@
         }
         try {
             clazz = Class.forName("java.security." + type + "Spi");
-            spiMap.put(type, clazz);
+            Class<?> tmpClazz = spiMap.putIfAbsent(type, clazz);
+            if(tmpClazz != null)
+                clazz = tmpClazz;
             return clazz;
         } catch (ClassNotFoundException e) {
             throw new AssertionError("Spi class not found", e);


More information about the jdk8-dev mailing list