2010-08-30 12 views
16

Dans le gestionnaire de sécurité par défaut, si je crée un ExecutorService (ThreadPoolExecutor dans ce cas), je ne peux pas le fermer, shutdown() appelle simplement checkPermission("modifyThread") et donc immédiatement meurt:Pourquoi ne puis-je pas éteindre mon propre ExecutorService sous SecurityManager?

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 A fil actuel: Thread [main, 5, main] Exception dans le thread "principal" java.security.AccessControlException: accès refusé (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 A fil courant: Thread [main, 5, main] Exception dans le thread "principal" java.security.AccessControlException: accès refusé (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)

Pourquoi ??????? Quels sont les implications de sécurité de la création d'un pool de threads que vous seul contrôle et l'arrêt? Est-ce un bug dans les implémentations, ou ai-je oublié quelque chose?

Voyons voir ce que la spécification pour ExecutorService.shutdown dit ...

lance un arrêt ordonné dans lequel les tâches sont exécutées précédemment soumises, mais aucune nouvelle tâche ne sera acceptée. L'invocation n'a aucun effet supplémentaire si elle est déjà arrêtée.

Lancers: SecurityException - si un gestionnaire de sécurité existe et la fermeture de ce ExecutorService peut manipuler les discussions que l'appelant ne soit pas autorisé à modifier, car il ne tient pas RuntimePermission (« modifyThread »), ou la méthode checkAccess du gestionnaire de sécurité refuse l'accès .

Cette ... est à peu près aussi vague que possible.La spécification dit rien de sur les "threads système" étant fait pendant le cycle de vie d'un ExecutorService et en outre, il vous permet de fournir vos propres threads qui est la preuve qu'il devrait y avoir aucun "threads système" impliqués lorsque vous faites cela. (Comme fait ci-dessus dans ma source d'échantillon)

Il semble que les implémenteurs Java SE ont vu qu'il est possible pour shutdown d'augmenter SecurityException, donc ils étaient comme, "oh ok je vais juste ajouter un contrôle de sécurité aléatoire ici pour conformité » ...

la chose est, la lecture sur la source OpenJDK (openjdk-6-src-b20-21_jun_2010), il se trouve que le ne façon tout fil est jamais créé, est en appelant votre fourni ThreadFactory (qui n'est jamais appelé dans mon test car je ne crée aucun travail, et je n'appelle pas prestartCoreThread ou preStartAllCoreThreads). Le contrôle de la sécurité est donc fait sans raison apparente dans le ThreadPoolExecutor de OpenJDK (comme cela se fait dans le soleil jdk-1.6, mais je n'ai pas la source):

/** 
* 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 est appelé avant de faire quoi que ce soit ...

/** 
* 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(); 
     } 
    } 
} 

comme vous pouvez le voir, il inconditionnellement invoque checkPermission(shutdownPerm) sur le gestionnaire de sécurité .... shutdownPerm est définie comme ... private static final RuntimePermission shutdownPerm = nouvelle RuntimePermission ("modifyThread");

... qui n'a absolument aucun sens pour autant que je peux le dire parce que modifyThread implique l'accès à threads du système, et il sont pas de fils du système en jeu ici, en fait, il n'y a pas de fils du tout parce que Je n'ai pas soumis de travail ou de pré-démarrage, et même s'il y avait des threads, ils seraient mes threads parce que je suis passé dans un ThreadFactory. La spécification ne dit rien sur la mort par magie, sauf que si les threads du système sont impliqués (ils ne le sont pas), il peut y avoir un SecurityException.

Fondamentalement, pourquoi ne puis-je simplement supprimer la ligne qui vérifie l'accès aux threads du système? Je ne vois aucune implication en matière de sécurité l'appelant. Et comment personne d'autre n'est-il tombé sur ce problème? J'ai vu un post sur un traqueur de problème où ils ont "résolu" ce problème en changeant un appel à shutdownNow à shutdown, évidemment, cela ne l'a pas arrangé pour eux.

+0

'[...] il vous permet de fournir vos propres threads, ce qui est la preuve qu'il ne devrait pas y avoir de "threads système" impliqués [...]' Il y a clairement un défaut dans votre raisonnement. Mais oui, cela pourrait être fait différemment. C'est ainsi que JSR166 définit l'API. –

+0

Hmm ... ressemble à quelque chose qui pourrait avoir à aller à http://bugs.sun.com/ –

+0

@Tom Hawtin: Ahh, je ne savais pas que les JSRs ont défini cette partie de SE, merci de le mentionner. Cependant, l'API n'est-elle pas seulement spécifiée par javadoc dans cette JSR? Dans ce cas, il dit toujours: "SecurityException - si un gestionnaire de sécurité existe et l'arrêt de ExecutorService peut manipuler des threads que l'appelant n'est pas autorisé à modifier car il ne contient pas RuntimePermission (" modifyThread ") ou la méthode checkAccess du gestionnaire de sécurité refuse l'accès. " Je ne vois aucune raison de manipuler les threads du système dans mon cas, et checkAccess (Thread) par défaut ne protège que les threads du système. –

Répondre

1

C'est assez simple: vous ne pouvez pas le faire dans le groupe de threads principal. Il est partiellement conçu pour les applets. Copier depuis la méthode d'arrêt pourquoi? Vous pouvez utiliser librement PrivilegedAction pour appeler l'arrêt si c'est un problème. Gardez à l'esprit que Thread.interrupt() aussi innocent, il peut également regarder throws SecurityException.

Pour répondre à la question: assurez-vous que vous accordez votre propre code les autorisations et vous êtes heureux. Alternativement "modifyThread" peut aussi être accordé librement, il est principalement utilisé par les applets. Comme pour le code non fiable: le code non approuvé n'est même pas supposé traiter des threads en dehors de ThreadGroup, donc fournissez l'API pour créer le ThreadPool, et autorisez l'arrêt pour tel créé par l'appelant. Vous pouvez accorder l'autorisation en fonction de l'appelant.

l'espoir que cela a aidé un peu (la quantité de points d'interrogation montre clairement la gêne causée par le désespoir et la plus grande, bien que)

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

Sons comme un paresseux et/ou la mise en œuvre en toute sécurité. Au lieu de vérifier si d'autres threads sont impliqués, il suppose simplement que certains sont. Mieux vaut lancer une exception de sécurité plutôt que de laisser un trou de sécurité potentiel.