diff --git a/jdk/src/share/classes/sun/misc/InnocuousThread.java b/jdk/src/share/classes/sun/misc/InnocuousThread.java new file mode 100644 index 0000000000000000000000000000000000000000..436e3ed8c4b95c3e9877e24e1c651eff3b177329 --- /dev/null +++ b/jdk/src/share/classes/sun/misc/InnocuousThread.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2013, 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 GNUNSAFE 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 WITHOUNSAFET + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICUNSAFELAR PUNSAFERPOSE. See the GNUNSAFE 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 GNUNSAFE 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 UNSAFESA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 UNSAFESA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.misc; + +import java.security.AccessControlContext; +import java.security.ProtectionDomain; + +/** + * A thread that has no permissions, is not a member of any user-defined + * ThreadGroup and supports the ability to erase ThreadLocals. + * + * @implNote Based on the implementation of InnocuousForkJoinWorkerThread. + */ +public final class InnocuousThread extends Thread { + private static final Unsafe UNSAFE; + private static final ThreadGroup THREADGROUP; + private static final AccessControlContext ACC; + private static final long THREADLOCALS; + private static final long INHERITABLETHREADLOCALS; + private static final long INHERITEDACCESSCONTROLCONTEXT; + + public InnocuousThread(Runnable target) { + super(THREADGROUP, target, "anInnocuousThread"); + UNSAFE.putOrderedObject(this, INHERITEDACCESSCONTROLCONTEXT, ACC); + eraseThreadLocals(); + } + + @Override + public ClassLoader getContextClassLoader() { + // always report system class loader + return ClassLoader.getSystemClassLoader(); + } + + @Override + public void setUncaughtExceptionHandler(UncaughtExceptionHandler x) { + // silently fail + } + + @Override + public void setContextClassLoader(ClassLoader cl) { + throw new SecurityException("setContextClassLoader"); + } + + // ensure run method is run only once + private volatile boolean hasRun; + + @Override + public void run() { + if (Thread.currentThread() == this && !hasRun) { + hasRun = true; + super.run(); + } + } + + /** + * Drops all thread locals (and inherited thread locals). + */ + public void eraseThreadLocals() { + UNSAFE.putObject(this, THREADLOCALS, null); + UNSAFE.putObject(this, INHERITABLETHREADLOCALS, null); + } + + // Use Unsafe to access Thread group and ThreadGroup parent fields + static { + try { + ACC = new AccessControlContext(new ProtectionDomain[] { + new ProtectionDomain(null, null) + }); + + // Find and use topmost ThreadGroup as parent of new group + UNSAFE = Unsafe.getUnsafe(); + Class tk = Thread.class; + Class gk = ThreadGroup.class; + + THREADLOCALS = UNSAFE.objectFieldOffset + (tk.getDeclaredField("threadLocals")); + INHERITABLETHREADLOCALS = UNSAFE.objectFieldOffset + (tk.getDeclaredField("inheritableThreadLocals")); + INHERITEDACCESSCONTROLCONTEXT = UNSAFE.objectFieldOffset + (tk.getDeclaredField("inheritedAccessControlContext")); + + long tg = UNSAFE.objectFieldOffset(tk.getDeclaredField("group")); + long gp = UNSAFE.objectFieldOffset(gk.getDeclaredField("parent")); + ThreadGroup group = (ThreadGroup) + UNSAFE.getObject(Thread.currentThread(), tg); + + while (group != null) { + ThreadGroup parent = (ThreadGroup)UNSAFE.getObject(group, gp); + if (parent == null) + break; + group = parent; + } + THREADGROUP = new ThreadGroup(group, "InnocuousThreadGroup"); + } catch (Exception e) { + throw new Error(e); + } + } +} diff --git a/jdk/src/share/classes/sun/nio/ch/Invoker.java b/jdk/src/share/classes/sun/nio/ch/Invoker.java index e55db542593c1d949848b568a21cb8ee3b25c90e..22908f0f143bb648b326d3c3768b4fcf8559ffd9 100644 --- a/jdk/src/share/classes/sun/nio/ch/Invoker.java +++ b/jdk/src/share/classes/sun/nio/ch/Invoker.java @@ -130,6 +130,18 @@ class Invoker { // clear interrupt Thread.interrupted(); + + // clear thread locals when in default thread pool + if (System.getSecurityManager() != null) { + Thread me = Thread.currentThread(); + if (me instanceof sun.misc.InnocuousThread) { + GroupAndInvokeCount thisGroupAndInvokeCount = myGroupAndInvokeCount.get(); + ((sun.misc.InnocuousThread)me).eraseThreadLocals(); + if (thisGroupAndInvokeCount != null) { + myGroupAndInvokeCount.set(thisGroupAndInvokeCount); + } + } + } } /** diff --git a/jdk/src/share/classes/sun/nio/ch/ThreadPool.java b/jdk/src/share/classes/sun/nio/ch/ThreadPool.java index 0761cb3b4baa0eea6c22c49d740b854e6551b59b..0624e8a678819a68287e40e8b314f431efb5db4f 100644 --- a/jdk/src/share/classes/sun/nio/ch/ThreadPool.java +++ b/jdk/src/share/classes/sun/nio/ch/ThreadPool.java @@ -27,6 +27,7 @@ package sun.nio.ch; import java.util.concurrent.*; import java.security.AccessController; +import java.security.PrivilegedAction; import sun.security.action.GetPropertyAction; import sun.security.action.GetIntegerAction; @@ -39,14 +40,6 @@ public class ThreadPool { "java.nio.channels.DefaultThreadPool.threadFactory"; private static final String DEFAULT_THREAD_POOL_INITIAL_SIZE = "java.nio.channels.DefaultThreadPool.initialSize"; - private static final ThreadFactory defaultThreadFactory = new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setDaemon(true); - return t; - } - }; private final ExecutorService executor; @@ -79,7 +72,22 @@ public class ThreadPool { } static ThreadFactory defaultThreadFactory() { - return defaultThreadFactory; + if (System.getSecurityManager() == null) { + return (Runnable r) -> { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + }; + } else { + return (Runnable r) -> { + PrivilegedAction action = () -> { + Thread t = new sun.misc.InnocuousThread(r); + t.setDaemon(true); + return t; + }; + return AccessController.doPrivileged(action); + }; + } } private static class DefaultThreadPoolHolder { @@ -100,7 +108,7 @@ public class ThreadPool { // default to thread factory that creates daemon threads ThreadFactory threadFactory = getDefaultThreadPoolThreadFactory(); if (threadFactory == null) - threadFactory = defaultThreadFactory; + threadFactory = defaultThreadFactory(); // create thread pool ExecutorService executor = Executors.newCachedThreadPool(threadFactory); return new ThreadPool(executor, false, initialSize);