-
Bug
-
Resolution: Fixed
-
P4
-
8, 11, 17, 19, 20
-
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