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

В диспетчере безопасности по умолчанию, если я создаю ExecutorService (в данном случае ThreadPoolExecutor ), я не могу его отключить, shutdown() просто вызывает checkPermission("modifyThread") и, следовательно, немедленноdies:

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 (AccessControl6.j.SecurityManager.checkPermission (SecurityManager.java:532) в java.util.concurrent.ThreadPoolExecutor.shutdown (ThreadPoolExecutor.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.checkPerj) в j55.util.concurrent.ThreadPoolExecutor.checkShutdownAccess (ThreadPoolExecutor.java:711) в java.util.concurrent.ThreadPoolExecutor.shutdown (ThreadPoolExecutor.java:1351) в A.main (A.java:22)

101021 * Почему ???????
Каковы последствия безопасности для создания пула потоков, который контролируется только вами , и его закрытия?Это ошибка в реализациях, или я что-то упустил?

Давайте посмотрим, что спецификация для ExecutorService.shutdown говорит ...

Инициируетупорядоченное отключение, при котором выполняются ранее представленные задачи, но новые задачи не принимаются.Вызов не имеет никакого дополнительного эффекта, если он уже выключен.

Броски: SecurityException - если менеджер безопасности существует, и закрытие этого ExecutorService может манипулировать потоками, которые вызывающей стороне не разрешено изменять, поскольку он не содержит RuntimePermission ("modifyThread"), или метод checkAccess менеджера безопасности запрещает доступ.

Это ... примерно так неопределенно, как и получается.В спецификации говорится ничего о любых "системных потоках", создаваемых в течение жизненного цикла ExecutorService, и, кроме того, позволяет вам предоставлять свои собственные потоки , что является доказательством того, что должно быть нет «системные потоки», когда вы делаете это.(Как это было сделано выше в моем примере источника)

Такое чувство, что разработчики Java SE увидели, что shutdown может поднять SecurityException, поэтому они были просто как, "о хорошо, я просто добавлюслучайная проверка безопасности на соответствие "...

Дело в том, что при чтении из источника OpenJDK (openjdk-6-src-b20-21_jun_2010) получается, что only way любой поток когда-либо создается, вызывая вашу поставляемую ThreadFactory (которая никогда не вызывается в моем тестовом примере, так как я не создаю никакой работы, и я не вызываю prestartCoreThread или prestartAllCoreThreads ).Таким образом, проверка безопасности выполняется без видимой причины в 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 =новый RuntimePermission ("modifyThread");

... что, насколько я могу судить, не имеет никакого смысла, потому что modifyThread подразумевает доступ к системным потокам , а не имеет системных потоков, которые здесь играютНа самом деле нет никаких потоков, потому что я не отправлял ни одной работы или предварительного запуска, и даже если бы были потоки, они были бы моими потоками, потому что я передал ThreadFactory.В спецификации ничего не говорится о магическом умирании, кроме того, что если задействованы системные потоки (они не участвуют), может быть SecurityException.

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

Ответы [ 2 ]

1 голос
/ 14 марта 2011

Все довольно просто: вы не можете сделать это в основной группе потоков.Он частично разработан для апплетов.Скопировать из метода выключения идеи, почему?Вы можете свободно использовать 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.)
     */
0 голосов
/ 14 марта 2011

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

...