简体   繁体   English

为什么我不能在SecurityManager下关闭自己的ExecutorService?

[英]Why can't I shutdown my own ExecutorService under a SecurityManager?

Under the default security manager, if I create an ExecutorService ( ThreadPoolExecutor in this case), I cannot shut it down, shutdown() just calls checkPermission("modifyThread") and thus immediately dies: 在默认安全管理器下,如果我创建一个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
    }
}

Sun JDK: Sun JDK:

$ java -Djava.security.manager A current thread: Thread[main,5,main] Exception in thread "main" java.security.AccessControlException: access denied (java.lang.RuntimePermission modifyThread) at java.security.AccessControlContext.checkPermission(AccessControlContext.java:323) at java.security.AccessController.checkPermission(AccessController.java:546) at java.lang.SecurityManager.checkPermission(SecurityManager.java:532) at java.util.concurrent.ThreadPoolExecutor.shutdown(ThreadPoolExecutor.java:1094) at A.main(A.java:22) $ java -Djava.security.manager当前线程:Thread [main,5,main]线程中的异常“main”java.security.AccessControlException:java.security.AccessControlContext.checkPermission中的access denied(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: OpenJDK的:

$ java -Djava.security.manager A current thread: Thread[main,5,main] Exception in thread "main" java.security.AccessControlException: access denied (java.lang.RuntimePermission modifyThread) at java.security.AccessControlContext.checkPermission(AccessControlContext.java:342) at java.security.AccessController.checkPermission(AccessController.java:553) at java.lang.SecurityManager.checkPermission(SecurityManager.java:549) at java.util.concurrent.ThreadPoolExecutor.checkShutdownAccess(ThreadPoolExecutor.java:711) at java.util.concurrent.ThreadPoolExecutor.shutdown(ThreadPoolExecutor.java:1351) at A.main(A.java:22) $ java -Djava.security.manager当前线程:Thread [main,5,main]线程中的异常“main”java.security.AccessControlException:java.security.AccessControlContext.checkPermission中的access denied(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)在A.main的java.util.concurrent.ThreadPoolExecutor.shutdown(ThreadPoolExecutor.java:1351)(A.java:22)

Why??????? 为什么??????? What are the security implications of creating a thread pool that only you control, and shutting it down? 创建只有您控制并关闭它的线程池有什么安全隐患? Is this a bug in the implementations, or am I missing something? 这是实施中的错误,还是我遗漏了什么?

Let's see what the spec for ExecutorService.shutdown says... 让我们看看ExecutorService.shutdown的规范是什么......

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 - if a security manager exists and shutting down this ExecutorService may manipulate threads that the caller is not permitted to modify because it does not hold RuntimePermission("modifyThread"), or the security manager's checkAccess method denies access. 抛出:SecurityException - 如果存在安全管理器并且关闭此ExecutorService可能会操纵不允许调用者修改的线程,因为它不包含RuntimePermission(“modifyThread”),或者安全管理器的checkAccess方法拒绝访问。

This... is about as vague as it gets. 这......就像它变得模糊一样。 The spec says nothing about any "system threads" being made during the life-cycle of an ExecutorService and furthermore, it lets you supply your own threads which is proof that there should be no "system threads" involved when you do that. 该规范没有提到任何“系统线程”一个ExecutorService的生命周期的过程中进行,此外, 它可以让你提供你自己的线程这是证明不应该 “系统线程”参与,当你做到这一点。 (As done above in my sample source) (正如我上面的示例源中所做的那样)

It feels like the Java SE implementors saw that it's possible for shutdown to raise SecurityException , so they were just like, "oh okay I'll just add a random security check here for compliance"... 感觉就像Java SE实现者看到shutdown可能会引发SecurityException ,所以他们就像是,“哦,好吧,我只是在这里添加随机安全检查以确保合规性”......

The thing is, reading over OpenJDK source (openjdk-6-src-b20-21_jun_2010), it turns out that the only way any thread is ever created, is by calling your supplied ThreadFactory (which is never called in my testcase since I don't create any work, and I don't call prestartCoreThread or preStartAllCoreThreads ). 通过阅读OpenJDK源代码(openjdk-6-src-b20-21_jun_2010),事实证明,创建任何线程的唯一方法是调用你提供的ThreadFactory (因为我不在我的测试用例中调用它) “产生任何工作,我不叫prestartCoreThreadpreStartAllCoreThreads )。 The security check is thus done for no apparent reason in OpenJDK's ThreadPoolExecutor (as is done in sun-jdk-1.6 but I don't have the source): 因此,在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 is called before doing anything... 在做任何事之前调用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();
        }
    }
}

As you can see, it unconditionally invokes checkPermission(shutdownPerm) on the security manager.... shutdownPerm is defined as... private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread"); 如您所见,它无条件地在安全管理器上调用checkPermission(shutdownPerm) .... shutdownPerm定义为... private static final RuntimePermission shutdownPerm = new RuntimePermission(“modifyThread”);

...which makes absolutely no sense as far as I can tell because modifyThread implies access to system threads , and there are no system threads in play here, in fact, there are no threads at all because I didn't submit any work or prestart, and even if there were threads, they'd be my threads because I passed in a ThreadFactory . ......这使得完全没有意义,因为据我可以告诉,因为modifyThread意味着访问系统线程 ,这里在游戏中没有系统线程,其实,有没有线程,因为在所有我没有提交任何工作或prestart,即使有线程,它们也是我的线程,因为我传入了一个ThreadFactory The spec doesn't say anything about magically dying, other than that if system threads are involved (they aren't), there could be a SecurityException . 该规范没有说明神奇的死亡,除了涉及系统线程(它们不是),可能有一个SecurityException

Basically, why can't I just remove the line that checks access to system threads? 基本上,为什么我不能删除检查系统线程访问权限的行? I see no security implication calling for it. 我认为没有安全隐含要求它。 And how has nobody else come across this issue??? 怎么没有其他人遇到这个问题? I've seen a post on an issue tracker where they "resolved" this issue by changing a call to shutdownNow to shutdown , obviously, that didn't fix it for them. 我已经看到了问题跟踪,他们通过改变调用“解决”这个问题后shutdownNowshutdown ,显然,这并没有为他们解决。

It's quite simple: you can't do it in the main thread group. 这很简单:你不能在主线程组中完成它。 It's partly designed for applets. 它部分是为applet设计的。 Copy from shutdown method idea why? 从关机方法想法复制为什么? You can freely use PrivilegedAction to call shutdown if that's an issue. 如果这是一个问题,您可以自由使用PrivilegedAction来调用shutdown。 Keep in mind that Thread.interrupt() as innocent it might look also throws SecurityException . 请记住,Thread.interrupt()作为无辜,它可能看起来也会throws SecurityException

To answer the question: just make sure you grant your own code the permissions and you're happy. 要回答这个问题:只需确保您授予自己的代码权限,您就会感到高兴。 Alternatively "modifyThread" can be granted freely as well, it's used mostly by applets. 或者“modifyThread”也可以自由授予,它主要由applet使用。

As for untrusted code: well, the untrusted code is not even supposed to deal w/ threads outside its ThreadGroup, so provide the API to create the ThreadPool, and allow shutdown for such created by the caller. 至于不受信任的代码:嗯,不受信任的代码甚至不应该处理其ThreadGroup之外的线程,因此提供API来创建ThreadPool,并允许关闭由调用者创建的。 You can GRANT the permission based on the caller. 您可以根据呼叫者授予权限。

Hope this helped a bit (the amount of question marks clearly shows desperation and utmost annoyance, though) 希望这有点帮助(问号的数量清楚地表明了绝望和最大的烦恼)

    /*
     * 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.)
     */

Sounds like a lazy and/or safe implementation. 听起来像一个懒惰和/或安全的实现。 Instead of checking if other threads are involved, it just assumes some are. 它不是检查是否涉及其他线程,而是假设有些线程。 Better to throw a security exception rather than leave a potential security hole. 最好抛出一个安全例外而不是留下潜在的安全漏洞。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM