16

在默认的安全管理器下,如果我创建一个ExecutorService(在这种情况下是ThreadPoolExecutor),我无法关闭它,shutdown()只是调用checkPermission("modifyThread")并因此立即死亡:

import java.util.concurrent.*;

class A {
    public static void main( String[] args) {
        Thread ct = Thread.currentThread();
        System.out.println("current thread: " + ct);
        ct.checkAccess(); // we have access to our own thread...
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(
            1, // one core thread
            1, // doesn't matter because queue is unbounded
            0, TimeUnit.SECONDS, // doesn't matter in this case
            new LinkedBlockingQueue<Runnable>(), /* unbound queue for
                                                  * our single thread */
            new ThreadFactory() {
                public Thread newThread(Runnable r) {
                    // obviously never gets called as we don't add any work
                    System.out.println("making thread");
                    return new Thread(r);
                }
            }
        );
        tpe.shutdown(); // raises security exception
    }
}

孙 JDK:

$ java -Djava.security.manager 当前线程:线程“main”中的 Thread[main,5,main] 异常 java.security.AccessControlException:java.security.AccessControlContext.checkPermission 的访问被拒绝(java.lang.RuntimePermission modifyThread) (AccessControlContext.java:323) 在 java.security.AccessController.checkPermission(AccessController.java:546) 在 java.lang.SecurityManager.checkPermission(SecurityManager.java:532) 在 java.util.concurrent.ThreadPoolExecutor.shutdown(ThreadPoolExecutor. java:1094) 在 A.main(A.java:22)

OpenJDK:

$ java -Djava.security.manager 当前线程:线程“main”中的 Thread[main,5,main] 异常 java.security.AccessControlException:java.security.AccessControlContext.checkPermission 的访问被拒绝(java.lang.RuntimePermission modifyThread) (AccessControlContext.java:342) 在 java.security.AccessController.checkPermission(AccessController.java:553) 在 java.lang.SecurityManager.checkPermission(SecurityManager.java:549) 在 java.util.concurrent.ThreadPoolExecutor.checkShutdownAccess(ThreadPoolExecutor. java:711) 在 java.util.concurrent.ThreadPoolExecutor.shutdown(ThreadPoolExecutor.java:1351) 在 A.main(A.java:22)

为什么???????创建一个只有你控制的线程池并关闭它有什么安全隐患?这是实现中的错误,还是我遗漏了什么?

让我们看看ExecutorService.shutdown的规范是怎么说的……

启动有序关闭,其中执行先前提交的任务,但不会接受新任务。如果已经关闭,调用没有额外的效果。

抛出:SecurityException - 如果安全管理器存在并且关闭此 ExecutorService 可能会操作不允许调用者修改的线程,因为它不持有 RuntimePermission("modifyThread"),或者安全管理器的 checkAccess 方法拒绝访问。

这……是最模糊的。该规范没有说明在 ExecutorService 的生命周期中创建的任何“系统线程”,此外,它允许您提供自己的线程,这证明当您这样做时不应该涉及“系统线程”。(如上所述在我的示例源中完成)

感觉就像 Java SE 实现者看到了shutdownraise是可能的SecurityException,所以他们就像,“哦,好吧,我将在这里添加一个随机安全检查以确保合规性”......

问题是,阅读 OpenJDK 源代码(openjdk-6-src-b20-21_jun_2010),事实证明,创建任何线程的唯一方法是调用您提供的ThreadFactory(在我的测试用例中从未调用过,因为我没有'不创建任何工作,我不调用prestartCoreThreadpreStartAllCoreThreads)。因此,在 OpenJDK 的 ThreadPoolExecutor 中没有明显的原因进行安全检查(就像在 sun-jdk-1.6 中所做的那样,但我没有源代码):

/**
 * Initiates an orderly shutdown in which previously submitted
 * tasks are executed, but no new tasks will be accepted.
 * Invocation has no additional effect if already shut down.
 *
 * @throws SecurityException {@inheritDoc}
 */
public void shutdown() {
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        checkShutdownAccess();
        advanceRunState(SHUTDOWN);
        interruptIdleWorkers();
        onShutdown(); // hook for ScheduledThreadPoolExecutor
    } finally {
        mainLock.unlock();
    }
    tryTerminate();
}

checkShutdownAccess在做任何事情之前被调用...

/**
 * If there is a security manager, makes sure caller has
 * permission to shut down threads in general (see shutdownPerm).
 * If this passes, additionally makes sure the caller is allowed
 * to interrupt each worker thread. This might not be true even if
 * first check passed, if the SecurityManager treats some threads
 * specially.
 */
private void checkShutdownAccess() {
    SecurityManager security = System.getSecurityManager();
    if (security != null) {
        security.checkPermission(shutdownPerm);
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            for (Worker w : workers)
                security.checkAccess(w.thread);
        } finally {
            mainLock.unlock();
        }
    }
}

如您所见,它无条件地调用checkPermission(shutdownPerm)安全管理器.... shutdownPerm 定义为... private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread");

...据我所知,这绝对没有意义,因为这modifyThread意味着访问系统线程,并且这里没有系统线程在起作用,事实上,根本没有线程,因为我没有提交任何工作或预启动,即使有线程,它们也会是我的线程,因为我传入了ThreadFactory. 规范没有说任何关于神奇死亡的内容,除了如果涉及系统线程(它们不是),可能会有一个SecurityException.

基本上,为什么我不能删除检查系统线程访问的行?我认为没有安全隐患需要它。还有其他人怎么没有遇到过这个问题???我在问题跟踪器上看到了一篇帖子,他们通过将调用更改为 来“解决”了这个问题shutdownNowshutdown显然,这并没有为他们解决这个问题。

4

2 回答 2

1

这很简单:你不能在主线程组中做到这一点。它部分是为小程序设计的。从关机方法的想法复制为什么?如果这是一个问题,您可以自由地使用 PrivilegedAction 来调用关机。请记住,Thread.interrupt() 看起来也很无辜throws SecurityException

要回答这个问题:只要确保您授予自己的代码权限并且您很高兴。或者“modifyThread”也可以自由授予,它主要由小程序使用。

至于不受信任的代码:好吧,不受信任的代码甚至不应该处理其 ThreadGroup 之外的线程,因此提供创建 ThreadPool 的 API,并允许关闭调用者创建的线程。您可以根据调用者授予权限。

希望这会有所帮助(尽管问号的数量清楚地表明了绝望和最大的烦恼)

    /*
     * Conceptually, shutdown is just a matter of changing the
     * runState to SHUTDOWN, and then interrupting any worker
     * threads that might be blocked in getTask() to wake them up
     * so they can exit. Then, if there happen not to be any
     * threads or tasks, we can directly terminate pool via
     * tryTerminate.  Else, the last worker to leave the building
     * turns off the lights (in workerDone).
     *
     * But this is made more delicate because we must cooperate
     * with the security manager (if present), which may implement
     * policies that make more sense for operations on Threads
     * than they do for ThreadPools. This requires 3 steps:
     *
     * 1. Making sure caller has permission to shut down threads
     * in general (see shutdownPerm).
     *
     * 2. If (1) passes, making sure the caller is allowed to
     * modify each of our threads. This might not be true even if
     * first check passed, if the SecurityManager treats some
     * threads specially. If this check passes, then we can try
     * to set runState.
     *
     * 3. If both (1) and (2) pass, dealing with inconsistent
     * security managers that allow checkAccess but then throw a
     * SecurityException when interrupt() is invoked.  In this
     * third case, because we have already set runState, we can
     * only try to back out from the shutdown as cleanly as
     * possible. Some workers may have been killed but we remain
     * in non-shutdown state (which may entail tryTerminate from
     * workerDone starting a new worker to maintain liveness.)
     */
于 2011-03-13T21:25:43.747 回答
0

听起来像一个懒惰和/或安全的实现。它不是检查是否涉及其他线程,而是假设一些是。最好抛出安全异常而不是留下潜在的安全漏洞。

于 2011-03-13T22:03:01.457 回答