Set thread context classloader in a doPrivileged block

Motivation:

In a few classes, Netty starts a thread and then sets the context classloader of these threads
to prevent classloader leaks. The Thread#setContextClassLoader method is a privileged method in
that it requires permissions to be executed when there is a security manager in place. Unless
these calls are wrapped in a doPrivileged block, they will fail in an environment with a security
manager and restrictive policy in place.

Modifications:

Wrap the calls to Thread#setContextClassLoader in a AccessController#doPrivileged block.

Result:

After this change, the threads can set the context classloader without any errors in an
environment with a security manager and restrictive policy in place.
This commit is contained in:
jaymode 2018-01-24 14:06:19 -07:00 committed by Norman Maurer
parent 848b2b23d7
commit 0eda1fea58
3 changed files with 30 additions and 6 deletions

View File

@ -22,6 +22,8 @@ import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
@ -106,13 +108,19 @@ public final class ThreadDeathWatcher {
pendingEntries.add(new Entry(thread, task, isWatch));
if (started.compareAndSet(false, true)) {
Thread watcherThread = threadFactory.newThread(watcher);
final Thread watcherThread = threadFactory.newThread(watcher);
// Set to null to ensure we not create classloader leaks by holds a strong reference to the inherited
// classloader.
// See:
// - https://github.com/netty/netty/issues/7290
// - https://bugs.openjdk.java.net/browse/JDK-7008595
watcherThread.setContextClassLoader(null);
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
watcherThread.setContextClassLoader(null);
return null;
}
});
watcherThread.start();
ThreadDeathWatcher.watcherThread = watcherThread;

View File

@ -18,6 +18,8 @@ package io.netty.util.concurrent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Queue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
@ -218,13 +220,19 @@ public final class GlobalEventExecutor extends AbstractScheduledEventExecutor {
private void startThread() {
if (started.compareAndSet(false, true)) {
Thread t = threadFactory.newThread(taskRunner);
final Thread t = threadFactory.newThread(taskRunner);
// Set to null to ensure we not create classloader leaks by holds a strong reference to the inherited
// classloader.
// See:
// - https://github.com/netty/netty/issues/7290
// - https://bugs.openjdk.java.net/browse/JDK-7008595
t.setContextClassLoader(null);
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
t.setContextClassLoader(null);
return null;
}
});
// Set the thread before starting it as otherwise inEventLoop() may return false and so produce
// an assert error.

View File

@ -21,6 +21,8 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@ -98,14 +100,20 @@ public final class ObjectCleaner {
// Check if there is already a cleaner running.
if (CLEANER_RUNNING.compareAndSet(false, true)) {
Thread cleanupThread = new FastThreadLocalThread(CLEANER_TASK);
final Thread cleanupThread = new FastThreadLocalThread(CLEANER_TASK);
cleanupThread.setPriority(Thread.MIN_PRIORITY);
// Set to null to ensure we not create classloader leaks by holding a strong reference to the inherited
// classloader.
// See:
// - https://github.com/netty/netty/issues/7290
// - https://bugs.openjdk.java.net/browse/JDK-7008595
cleanupThread.setContextClassLoader(null);
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
cleanupThread.setContextClassLoader(null);
return null;
}
});
cleanupThread.setName(CLEANER_THREAD_NAME);
// This Thread is not a daemon as it will die once all references to the registered Objects will go away