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 現在のスレッド: 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) で 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 現在のスレッド: 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) で 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 の実装者は、 がshutdownをレイズすることが可能であることを認識したように感じられるSecurityExceptionので、「オーケー、準拠のためにここにランダムなセキュリティ チェックを追加します」というようなものでした...

問題は、OpenJDK ソース (openjdk-6-src-b20-21_jun_2010) を読むと、スレッドを作成する唯一の方法は、提供された 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 = new RuntimePermission("modifyThread");

...システムスレッドmodifyThreadへのアクセスを意味するため、私が知る限り、これはまったく意味がありません。また、ここではシステムスレッドが実行されていません。実際、作業や事前開始を送信しなかったため、スレッドはまったくありません、スレッドがあったとしても、.を渡したので、それらは私のスレッドになります。システムスレッドが関与している場合(関与していない場合)、.ThreadFactorySecurityException

基本的に、システム スレッドへのアクセスをチェックする行を削除できないのはなぜですか? それを必要とするセキュリティ上の意味はありません。そして、他の誰もこの問題に出くわしたのはどうしてですか??? への呼び出しを変更することでこの問題を「解決」した問題トラッカーの投稿を見たことがありますがshutdownNowshutdown明らかに修正されませんでした。

4

2 に答える 2

1

それは非常に簡単です: メイン スレッド グループでは実行できません。一部はアプレット用に設計されています。シャットダウン方法のアイデアからコピーする理由は? それが問題である場合は、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.)
     */
于 2011-03-13T21:25:43.747 に答える
0

怠惰で安全な実装のように聞こえます。他のスレッドが関与しているかどうかを確認する代わりに、いくつかのスレッドが関与していると想定します。潜在的なセキュリティ ホールを残すよりも、セキュリティ例外をスローすることをお勧めします。

于 2011-03-13T22:03:01.457 に答える