Duplicate :
|
|
Duplicate :
|
|
Relates :
|
FULL PRODUCT VERSION : java version "1.8.0_112" Java(TM) SE Runtime Environment (build 1.8.0_112-b15) Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode) FULL OS VERSION : OS Name: Microsoft Windows 7 Enterprise OS Version: 6.1.7601 Service Pack 1 Build 7601 EXTRA RELEVANT SYSTEM CONFIGURATION : System Manufacturer: VMware, Inc. System Model: VMware Virtual Platform System Type: x64-based PC Processor(s): 2 CPUs x Intel64 Family 6 Model 62 Stepping 4 GenuineIntel ~2693 A DESCRIPTION OF THE PROBLEM : In a multi-threaded scenario it is possible that a finalizer performing Arrays.fill() on an instance field may actually mangle the contents of a fresh array allocated by a different thread. The problem was originally reported on StackOverflow as https://stackoverflow.com/q/46971788/1654233 where user was getting sporadic failures while trying to decrypt some data in multiple threads with PBKDF2WithHmacSHA1 algorithm. Debugging revealed that com.sun.crypto.provider.PBKDF2KeyImpl.getEncoded() method sometimes returns a clone of the field key:byte[] populated with all zeroes which seems to be impossible in the scenario. After some research it become clear that the problem can be reproduced with the snippet below and the only thing that matters is an array operation performed in finalizer. THE PROBLEM WAS REPRODUCIBLE WITH -Xint FLAG: No THE PROBLEM WAS REPRODUCIBLE WITH -server FLAG: Yes STEPS TO FOLLOW TO REPRODUCE THE PROBLEM : Compile and run the test case with -Xmx10m (can be a larger number but this produces error in a more predictable manner). REPRODUCIBILITY : This bug can be reproduced occasionally. ---------- BEGIN SOURCE ---------- package bugs; import javax.xml.bind.DatatypeConverter; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Random; import java.util.stream.IntStream; public class NewlyAllocatedArrayFilledByOtherInstanceFinalizer { void demonstrate() throws Exception { final int iterations = 1000000; IntStream.range(0, iterations) .parallel() .forEach(i -> { String expectedValue = randomAlphaNumeric(10); byte[] expectedBytes = expectedValue.getBytes(StandardCharsets.UTF_8); ArrayHolder holder = new ArrayHolder(expectedBytes); byte[] actualBytes = holder.getBytes(); if (!Arrays.equals(expectedBytes, actualBytes)) { String actualValue = new String(actualBytes, StandardCharsets.UTF_8); System.err.printf("Assertion failed: expected='%s' actual='%s' (bytes: %s)%n", expectedValue, actualValue, DatatypeConverter.printHexBinary(actualBytes)); } }); } static class ArrayHolder { private byte[] _bytes; ArrayHolder(final byte[] bytes) { _bytes = bytes.clone(); } byte[] getBytes() { return _bytes.clone(); } @Override protected void finalize() throws Throwable { if (_bytes != null) { Arrays.fill(_bytes, (byte) 'z'); _bytes = null; } super.finalize(); } } private static final String ALPHA_NUMERIC_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; private static final Random RND = new Random(); static String randomAlphaNumeric(int count) { final StringBuilder sb = new StringBuilder(); while (count-- != 0) { int character = RND.nextInt(ALPHA_NUMERIC_STRING.length()); sb.append(ALPHA_NUMERIC_STRING.charAt(character)); } return sb.toString(); } public static void main(String[] args) throws Exception { new NewlyAllocatedArrayFilledByOtherInstanceFinalizer().demonstrate(); } } ---------- END SOURCE ---------- CUSTOMER SUBMITTED WORKAROUND : Not identified.
|