Почему я не могу закрыть свой собственный ExecutorService в SecurityManager?

В диспетчере безопасности по умолчанию, если я создаю 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:

$ java -Djava.security.manager Текущий поток: Thread[main,5,main] Исключение в потоке "main" java.security.AccessControlException: доступ запрещен (java.lang.RuntimePermission modifyThread) в java.security.AccessControlContext.checkPermission(AccessControlContext.java:323) в java.security.AccessController.checkPermission(AccessController.java:546) в java.lang.SecurityManager.checkPermission(SecurityManager.java:532) в java.util.concurrent.ThreadPoolececutor Java:1094) в A.main(A.java:22)

OpenJDK:

$ java -Djava.security.manager Текущий поток: Thread[main,5,main] Исключение в потоке "main" java.security.AccessControlException: доступ запрещен (java.lang.RuntimePermission modifyThread) в java.security.AccessControlContext.checkPermission(AccessControlContext.java:342) в java.security.AccessController.checkPermission(AccessController.java:553) в java.lang.SecurityManager.checkPermission(SecurityManager.java:549) в java.util.concurrent.ThreadPecol.Exec. 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 увидели, что это возможно для shutdown поднимать SecurityException, так что они были просто как, "ну ладно, я просто добавлю случайную проверку безопасности здесь для соответствия"...

Дело в том, что, читая исходный код OpenJDK (openjdk-6-src-b20-21_jun_2010), оказывается, что единственный способ создания любого потока - это вызов вашей поставляемой ThreadFactory (которая никогда не вызывается в моем тестовом примере, так как я не не создавать никаких работ, и я не вызываю prestartCoreThread или preStartAllCoreThreads). Таким образом, проверка безопасности выполняется без видимой причины в ThreadPoolExecutor OpenJDK (как это делается в 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,

По сути, почему я не могу просто удалить строку, которая проверяет доступ к системным потокам? Я не вижу никаких последствий для безопасности, призывающих к этому. И как еще никто не сталкивался с этим вопросом??? Я видел сообщение на трекере, где они "решили" эту проблему, изменив вызов на shutdownNow в shutdown очевидно, это не исправило их.

2 ответа

Это довольно просто: вы не можете сделать это в основной группе потоков. Он частично разработан для апплетов. Скопировать из метода выключения идеи почему? Вы можете свободно использовать PrivilegedAction для вызова отключения, если это проблема. Имейте в виду, что Thread.interrupt() может показаться невинным throws SecurityException,

Чтобы ответить на вопрос: просто убедитесь, что вы предоставили свой собственный код разрешения, и вы счастливы. В качестве альтернативы "modifyThread" также может быть предоставлен свободно, он используется в основном апплетами.

Что касается ненадежного кода: ну, ненадежный код даже не должен обрабатывать потоки вне своей ThreadGroup, поэтому предоставьте API для создания ThreadPool и разрешите отключение для такого, созданного вызывающей стороной. Вы можете предоставить разрешение на основе звонящего.

Надеюсь, это немного помогло (хотя количество вопросительных знаков ясно показывает отчаяние и крайнюю досаду)

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

Походит на ленивую и / или безопасную реализацию. Вместо проверки, вовлечены ли другие потоки, он просто предполагает, что некоторые из них. Лучше бросить исключение безопасности, чем оставить потенциальную дыру в безопасности.

Другие вопросы по тегам