--- old/src/share/classes/java/security/SecureRandom.java 2016-11-03 11:00:03.208953100 +0800 +++ new/src/share/classes/java/security/SecureRandom.java 2016-11-03 11:00:02.877661500 +0800 @@ -32,6 +32,7 @@ import sun.security.jca.*; import sun.security.jca.GetInstance.Instance; +import sun.security.provider.ThreadSafeSecureRandomSpi; import sun.security.util.Debug; /** @@ -423,8 +424,14 @@ * * @see #getSeed */ - synchronized public void setSeed(byte[] seed) { - secureRandomSpi.engineSetSeed(seed); + public void setSeed(byte[] seed) { + if (secureRandomSpi instanceof ThreadSafeSecureRandomSpi) { + secureRandomSpi.engineSetSeed(seed); + } else { + synchronized (this) { + secureRandomSpi.engineSetSeed(seed); + } + } } /** @@ -449,7 +456,7 @@ * yet been initialized at that point. */ if (seed != 0) { - secureRandomSpi.engineSetSeed(longToByteArray(seed)); + setSeed(longToByteArray(seed)); } } @@ -465,7 +472,13 @@ */ @Override public void nextBytes(byte[] bytes) { - secureRandomSpi.engineNextBytes(bytes); + if (secureRandomSpi instanceof ThreadSafeSecureRandomSpi) { + secureRandomSpi.engineNextBytes(bytes); + } else { + synchronized (this) { + secureRandomSpi.engineNextBytes(bytes); + } + } } /** --- old/src/share/classes/sun/security/pkcs11/P11SecureRandom.java 2016-11-03 11:00:06.016851700 +0800 +++ new/src/share/classes/sun/security/pkcs11/P11SecureRandom.java 2016-11-03 11:00:05.662167800 +0800 @@ -28,6 +28,7 @@ import java.io.*; import java.security.*; import sun.security.pkcs11.wrapper.*; +import sun.security.provider.ThreadSafeSecureRandomSpi; /** * SecureRandom implementation class. Some tokens support only @@ -45,7 +46,8 @@ * @author Andreas Sterbenz * @since 1.5 */ -final class P11SecureRandom extends SecureRandomSpi { +final class P11SecureRandom extends SecureRandomSpi + implements ThreadSafeSecureRandomSpi { private static final long serialVersionUID = -8939510236124553291L; --- old/src/share/classes/sun/security/provider/SecureRandom.java 2016-11-03 11:00:08.601563500 +0800 +++ new/src/share/classes/sun/security/provider/SecureRandom.java 2016-11-03 11:00:08.272212400 +0800 @@ -53,7 +53,7 @@ */ public final class SecureRandom extends SecureRandomSpi -implements java.io.Serializable { + implements java.io.Serializable, ThreadSafeSecureRandomSpi { private static final long serialVersionUID = 3581829991155417889L; --- old/src/solaris/classes/sun/security/provider/NativePRNG.java 2016-11-03 11:00:11.199442600 +0800 +++ new/src/solaris/classes/sun/security/provider/NativePRNG.java 2016-11-03 11:00:10.867909100 +0800 @@ -73,7 +73,8 @@ * @since 1.5 * @author Andreas Sterbenz */ -public final class NativePRNG extends SecureRandomSpi { +public final class NativePRNG extends SecureRandomSpi + implements ThreadSafeSecureRandomSpi { private static final long serialVersionUID = -6599091113397072932L; @@ -238,7 +239,8 @@ * * @since 1.8 */ - public static final class Blocking extends SecureRandomSpi { + public static final class Blocking extends SecureRandomSpi + implements ThreadSafeSecureRandomSpi { private static final long serialVersionUID = -6396183145759983347L; private static final RandomIO INSTANCE = initIO(Variant.BLOCKING); @@ -287,7 +289,8 @@ * * @since 1.8 */ - public static final class NonBlocking extends SecureRandomSpi { + public static final class NonBlocking extends SecureRandomSpi + implements ThreadSafeSecureRandomSpi { private static final long serialVersionUID = -1102062982994105487L; private static final RandomIO INSTANCE = initIO(Variant.NONBLOCKING); --- old/src/windows/classes/sun/security/mscapi/PRNG.java 2016-11-03 11:00:13.819155200 +0800 +++ new/src/windows/classes/sun/security/mscapi/PRNG.java 2016-11-03 11:00:13.494921500 +0800 @@ -25,6 +25,8 @@ package sun.security.mscapi; +import sun.security.provider.ThreadSafeSecureRandomSpi; + import java.security.ProviderException; import java.security.SecureRandomSpi; @@ -35,7 +37,7 @@ */ public final class PRNG extends SecureRandomSpi - implements java.io.Serializable { + implements java.io.Serializable, ThreadSafeSecureRandomSpi { private static final long serialVersionUID = 4129268715132691532L; --- /dev/null 2016-11-03 11:00:16.000000000 +0800 +++ new/src/share/classes/sun/security/provider/ThreadSafeSecureRandomSpi.java 2016-11-03 11:00:16.067264600 +0800 @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.provider; + +/** + * A marker interface that advertises a {@code SecureRandom} implementation + * is thread safe. + */ +public interface ThreadSafeSecureRandomSpi { +} --- /dev/null 2016-11-03 11:00:18.000000000 +0800 +++ new/test/java/security/SecureRandom/NoSync.java 2016-11-03 11:00:18.212078200 +0800 @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import sun.security.provider.ThreadSafeSecureRandomSpi; + +import java.lang.reflect.Field; +import java.security.Provider; +import java.security.SecureRandom; +import java.security.Security; +import java.util.Date; +import java.util.concurrent.atomic.AtomicBoolean; + +/* + * @test + * @bug 7004967 + * @run main/othervm NoSync + * @summary SecureRandom should be more explicit about threading + */ +public class NoSync { + + static Field srs = null; + + public static void main(String[] args) throws Exception { + srs = SecureRandom.class.getDeclaredField("secureRandomSpi"); + srs.setAccessible(true); + for (Provider p : Security.getProviders()) { + for (Provider.Service s : p.getServices()) { + if (s.getType().equals("SecureRandom") && + !s.getAlgorithm().contains("Block")) { + test(SecureRandom.getInstance(s.getAlgorithm(), p)); + } + } + } + } + + static void test(SecureRandom sr) throws Exception { + test(sr, 20, 3000); + // All out-of-box impl should be thread safe + Object obj = srs.get(sr); + if (!(obj instanceof ThreadSafeSecureRandomSpi)) { + throw new Exception("Not ThreadSafe: " + obj.getClass()); + } + } + + public static void test(SecureRandom sr, int tnum, int rnum) + throws Exception { + + System.out.println(sr); + System.out.println(sr.getAlgorithm() + " " + sr.getProvider().getName()); + + System.out.println(new Date()); + Thread[] threads = new Thread[tnum]; + AtomicBoolean failed = new AtomicBoolean(false); + Thread.UncaughtExceptionHandler h = (t, e) -> { + failed.set(true); + e.printStackTrace(); + }; + for (int i = 0; i < threads.length; i++) { + threads[i] = new Thread() { + @Override + public void run() { + for (int j = 0; j < rnum; j++) { + sr.nextBytes(new byte[j%100+100]); + sr.setSeed((long)j); + } + } + }; + threads[i].setUncaughtExceptionHandler(h); + threads[i].start(); + } + for (int i = 0; i < threads.length; i++) { + threads[i].join(); + } + System.out.println(new Date()); + System.out.println(); + if (failed.get()) { + throw new RuntimeException("Failed"); + } + } +} --- /dev/null 2016-11-03 11:00:20.000000000 +0800 +++ new/test/java/security/SecureRandom/ThreadSafe.java 2016-11-03 11:00:20.314358300 +0800 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import sun.security.provider.ThreadSafeSecureRandomSpi; + +import java.security.Provider; +import java.security.SecureRandom; +import java.security.SecureRandomSpi; + +/* + * @test + * @bug 7004967 + * @summary SecureRandom should be more explicit about threading + */ +public class ThreadSafe { + public static void main(String[] args) throws Exception { + Provider p = new P(); + NoSync.test(SecureRandom.getInstance("S1", p), 5, 5); + try { + NoSync.test(SecureRandom.getInstance("S2", p), 5, 5); + throw new Exception("Failed"); + } catch (RuntimeException re) { + // Good + } + NoSync.test(SecureRandom.getInstance("S3", p), 5, 5); + try { + NoSync.test(SecureRandom.getInstance("S4", p), 5, 5); + throw new Exception("Failed"); + } catch (RuntimeException re) { + // Good + } + } + + public static class P extends Provider { + public P() { + + super("P", 1.0d, "Haha"); + + // Good. No attribute. + put("SecureRandom.S1", S.class.getName()); + + // Bad. Boasting ThreadSafe but isn't + put("SecureRandom.S2", ST.class.getName()); + + // Good. No attribute. + putService(new Service(this, "SecureRandom", "S3", + S.class.getName(), null, null)); + + // Bad. Boasting ThreadSafe but isn't + putService(new Service(this, "SecureRandom", "S4", + ST.class.getName(), null, null)); + } + } + + // This implementation is not itself thread safe. + public static class S extends SecureRandomSpi { + @java.lang.Override + protected void engineSetSeed(byte[] seed) { + return; + } + + private volatile boolean inCall = false; + @Override + protected void engineNextBytes(byte[] bytes) { + if (inCall) { + throw new RuntimeException("IN CALL"); + } + inCall = true; + try { + Thread.sleep(100); + } catch (Exception e) { + // OK + } + inCall = false; + } + + @Override + protected byte[] engineGenerateSeed(int numBytes) { + return new byte[numBytes]; + } + } + + // This implementation is not itself thread safe but boast so. + public static class ST extends S implements ThreadSafeSecureRandomSpi { + } +}