1/*
2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.internal.misc;
27
28import java.security.AccessControlContext;
29import java.security.AccessController;
30import java.security.ProtectionDomain;
31import java.security.PrivilegedAction;
32import java.util.concurrent.atomic.AtomicInteger;
33
34/**
35 * A thread that has no permissions, is not a member of any user-defined
36 * ThreadGroup and supports the ability to erase ThreadLocals.
37 */
38public final class InnocuousThread extends Thread {
39    private static final jdk.internal.misc.Unsafe UNSAFE;
40    private static final long THREAD_LOCALS;
41    private static final long INHERITABLE_THREAD_LOCALS;
42    private static final ThreadGroup INNOCUOUSTHREADGROUP;
43    private static final AccessControlContext ACC;
44    private static final long INHERITEDACCESSCONTROLCONTEXT;
45    private static final long CONTEXTCLASSLOADER;
46
47    private static final AtomicInteger threadNumber = new AtomicInteger(1);
48    private static String newName() {
49        return "InnocuousThread-" + threadNumber.getAndIncrement();
50    }
51
52    /**
53     * Returns a new InnocuousThread with an auto-generated thread name
54     * and its context class loader is set to the system class loader.
55     */
56    public static Thread newThread(Runnable target) {
57        return newThread(newName(), target);
58    }
59
60    /**
61     * Returns a new InnocuousThread with its context class loader
62     * set to the system class loader.
63     */
64    public static Thread newThread(String name, Runnable target) {
65        return new InnocuousThread(INNOCUOUSTHREADGROUP,
66                                   target,
67                                   name,
68                                   ClassLoader.getSystemClassLoader());
69    }
70
71    /**
72     * Returns a new InnocuousThread with an auto-generated thread name.
73     * Its context class loader is set to null.
74     */
75    public static Thread newSystemThread(Runnable target) {
76        return newSystemThread(newName(), target);
77    }
78
79    /**
80     * Returns a new InnocuousThread with null context class loader.
81     */
82    public static Thread newSystemThread(String name, Runnable target) {
83        return new InnocuousThread(INNOCUOUSTHREADGROUP,
84                                   target, name, null);
85    }
86
87    private InnocuousThread(ThreadGroup group, Runnable target, String name, ClassLoader tccl) {
88        super(group, target, name, 0L, false);
89        UNSAFE.putObjectRelease(this, INHERITEDACCESSCONTROLCONTEXT, ACC);
90        UNSAFE.putObjectRelease(this, CONTEXTCLASSLOADER, tccl);
91    }
92
93    @Override
94    public void setUncaughtExceptionHandler(UncaughtExceptionHandler x) {
95        // silently fail
96    }
97
98    @Override
99    public void setContextClassLoader(ClassLoader cl) {
100        // Allow clearing of the TCCL to remove the reference to the system classloader.
101        if (cl == null)
102            super.setContextClassLoader(null);
103        else
104            throw new SecurityException("setContextClassLoader");
105    }
106
107    /**
108     * Drops all thread locals (and inherited thread locals).
109     */
110    public final void eraseThreadLocals() {
111        UNSAFE.putObject(this, THREAD_LOCALS, null);
112        UNSAFE.putObject(this, INHERITABLE_THREAD_LOCALS, null);
113    }
114
115    // ensure run method is run only once
116    private volatile boolean hasRun;
117
118    @Override
119    public void run() {
120        if (Thread.currentThread() == this && !hasRun) {
121            hasRun = true;
122            super.run();
123        }
124    }
125
126    // Use Unsafe to access Thread group and ThreadGroup parent fields
127    static {
128        try {
129            ACC = new AccessControlContext(new ProtectionDomain[] {
130                new ProtectionDomain(null, null)
131            });
132
133            // Find and use topmost ThreadGroup as parent of new group
134            UNSAFE = jdk.internal.misc.Unsafe.getUnsafe();
135            Class<?> tk = Thread.class;
136            Class<?> gk = ThreadGroup.class;
137
138            THREAD_LOCALS = UNSAFE.objectFieldOffset(tk, "threadLocals");
139            INHERITABLE_THREAD_LOCALS = UNSAFE.objectFieldOffset
140                    (tk, "inheritableThreadLocals");
141            INHERITEDACCESSCONTROLCONTEXT = UNSAFE.objectFieldOffset
142                (tk, "inheritedAccessControlContext");
143            CONTEXTCLASSLOADER = UNSAFE.objectFieldOffset
144                (tk, "contextClassLoader");
145
146            long tg = UNSAFE.objectFieldOffset(tk, "group");
147            long gp = UNSAFE.objectFieldOffset(gk, "parent");
148            ThreadGroup group = (ThreadGroup)
149                UNSAFE.getObject(Thread.currentThread(), tg);
150
151            while (group != null) {
152                ThreadGroup parent = (ThreadGroup)UNSAFE.getObject(group, gp);
153                if (parent == null)
154                    break;
155                group = parent;
156            }
157            final ThreadGroup root = group;
158            INNOCUOUSTHREADGROUP = AccessController.doPrivileged(
159                new PrivilegedAction<ThreadGroup>() {
160                    @Override
161                    public ThreadGroup run() {
162                        return new ThreadGroup(root, "InnocuousThreadGroup");
163                    }
164                });
165        } catch (Exception e) {
166            throw new Error(e);
167        }
168    }
169}
170