- 
    Type:
Bug
 - 
    Resolution: Fixed
 - 
    Priority:
  P4                     
     - 
    Affects Version/s: 8, 11, 17, 19, 20
 - 
    Component/s: security-libs
 
- 
        b09
 - 
        generic
 - 
        generic
 - 
        Verified
 
| Issue | Fix Version | Assignee | Priority | Status | Resolution | Resolved In Build | 
|---|---|---|---|---|---|---|
| JDK-8344384 | 17.0.14 | Goetz Lindenmaier | P4 | Resolved | Fixed | b04 | 
                    A DESCRIPTION OF THE PROBLEM :
The tempArray in MessageDigestSpi is shallow copied on clone, thus clones of MessageDigestSpi are not thread-safe.
The error is very unlikely to happen in reality as tempArray is only allocated if engineUpdate was called with a ByteBuffer that is not array backed before clone.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See test example code
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Cloning MessageDigest objects results in two objects that are safe to be used in different threads.
ACTUAL -
Cloned MessageDigest can share some memory and thus influence their behaviour.
---------- BEGIN SOURCE ----------
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Random;
public class CloneDigest {
public static void main(final String[] args) throws Exception {
final var d1 = MessageDigest.getInstance("MD5");
final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
final var r = new Random(1024);
fillBuffer(r, buffer);
d1.update(buffer); // this statement triggers tempArray allocation
final var d2 = (MessageDigest) d1.clone();
assert Arrays.equals(d1.digest(), d2.digest());
final var t1 = updateThread(d1);
final var t2 = updateThread(d2);
t1.join();
t2.join();
if (!Arrays.equals(d1.digest(), d2.digest())) {
throw new AssertionError("digests differ");
// cloned digest shares some memory with original one
// and is thus not thread safe
}
}
private static void fillBuffer(final Random r, final ByteBuffer buffer) {
final byte[] bytes = new byte[buffer.capacity()];
r.nextBytes(bytes);
buffer.clear();
buffer.put(bytes);
buffer.flip();
}
public static Thread updateThread(final MessageDigest d) {
final var t = new Thread(() -> {
final var r = new Random(1024);
final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
for (int i = 0; i < 1024; i++) {
fillBuffer(r, buffer);
d.update(buffer);
}
});
t.start();
return t;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Do not clone MessageDigest that were already updated.
FREQUENCY : always
            
The tempArray in MessageDigestSpi is shallow copied on clone, thus clones of MessageDigestSpi are not thread-safe.
The error is very unlikely to happen in reality as tempArray is only allocated if engineUpdate was called with a ByteBuffer that is not array backed before clone.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
See test example code
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Cloning MessageDigest objects results in two objects that are safe to be used in different threads.
ACTUAL -
Cloned MessageDigest can share some memory and thus influence their behaviour.
---------- BEGIN SOURCE ----------
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Random;
public class CloneDigest {
public static void main(final String[] args) throws Exception {
final var d1 = MessageDigest.getInstance("MD5");
final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
final var r = new Random(1024);
fillBuffer(r, buffer);
d1.update(buffer); // this statement triggers tempArray allocation
final var d2 = (MessageDigest) d1.clone();
assert Arrays.equals(d1.digest(), d2.digest());
final var t1 = updateThread(d1);
final var t2 = updateThread(d2);
t1.join();
t2.join();
if (!Arrays.equals(d1.digest(), d2.digest())) {
throw new AssertionError("digests differ");
// cloned digest shares some memory with original one
// and is thus not thread safe
}
}
private static void fillBuffer(final Random r, final ByteBuffer buffer) {
final byte[] bytes = new byte[buffer.capacity()];
r.nextBytes(bytes);
buffer.clear();
buffer.put(bytes);
buffer.flip();
}
public static Thread updateThread(final MessageDigest d) {
final var t = new Thread(() -> {
final var r = new Random(1024);
final ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
for (int i = 0; i < 1024; i++) {
fillBuffer(r, buffer);
d.update(buffer);
}
});
t.start();
return t;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Do not clone MessageDigest that were already updated.
FREQUENCY : always
- backported by
 - 
                    
JDK-8344384 java.security.MessageDigestSpi clone can result in thread-unsafe clones
-         
     - Resolved
 
 -         
 
- links to
 - 
                    
        
        Commit
        openjdk/jdk/2e2e71e1
    
 - 
                    
        
        Commit(master)
        openjdk/jdk17u-dev/49b1ca94
    
 - 
                    
        
        Review(master)
        openjdk/jdk17u-dev/3039
    
 - 
                    
        
        Review(master)
        openjdk/jdk/12348