java security securitymanager jsr166

java - ¿Por qué no puedo cerrar mi propio ExecutorService bajo un SecurityManager?



jsr166 (2)

Es bastante simple: no puedes hacerlo en el grupo de hilos principal. Está parcialmente diseñado para applets. Copia de la idea de método de apagado por qué? Puede usar PrivilegedAction libremente para llamar a shutdown si eso es un problema. Tenga en cuenta que Thread.interrupt (), por inocente que pueda parecer, también throws SecurityException .

Para responder a la pregunta: solo asegúrate de otorgar los permisos a tu propio código y estarás contento. Alternativamente, "modifyThread" también se puede otorgar libremente, se usa principalmente en applets.

En cuanto al código que no es de confianza: bueno, el código que no es de confianza ni siquiera se supone que trata con subprocesos fuera de su Grupo de subprocesos, así que proporcione la API para crear el ThreadPool y permita el cierre de los creados por el llamante. Puede conceder el permiso basado en la persona que llama.

Espero que esto haya ayudado un poco (aunque la cantidad de signos de interrogación muestra claramente la desesperación y la mayor molestia)

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

Bajo el administrador de seguridad predeterminado, si creo un ExecutorService ( ThreadPoolExecutor en este caso), no puedo apagarlo, shutdown() solo llama a checkPermission("modifyThread") y por lo tanto muere inmediatamente:

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 Un subproceso actual: Subproceso [main, 5, main] Excepción en el subproceso "main" java.security.AccessControlException: acceso denegado (java.lang.RuntimePermission modifyThread) en java.security.AccessControlContext.checkPermission (AccessControlContext.java:323) en java.security.AccessController.checkPermission (AccessController.java:546) en java.lang.SecurityManager.checkPermissionPacioExplorador de facturas de las personas de la que se dedica el asunto (SecurityManager.java:532) en java.util.concurrent. java: 1094) en A.main (A.java:22)

OpenJDK:

$ java -Djava.security.manager Un subproceso actual: Subproceso [main, 5, main] Excepción en el subproceso "main" java.security.AccessControlException: acceso denegado (java.lang.RuntimePermission modifyThread) en java.security.AccessControlContext.checkPermission (AccessControlContext.java:342) en java.security.AccessController.checkPermission (AccessController.java:553) en java.lang.SecurityManager.checkPermission (en español). java: 711) en java.util.concurrent.ThreadPoolExecutor.shutdown (ThreadPoolExecutor.java:1351) en A.main (A.java:22)

¿¿¿¿¿¿¿Por qué??????? ¿Cuáles son las implicaciones de seguridad de crear un grupo de subprocesos que solo tú controlas y apagar? ¿Es esto un error en las implementaciones, o me estoy perdiendo algo?

Veamos lo que dice la especificación para ExecutorService.shutdown ...

Inicia un cierre ordenado en el que se ejecutan las tareas enviadas anteriormente, pero no se aceptarán nuevas tareas. La invocación no tiene ningún efecto adicional si ya está apagado.

Emite: SecurityException: si existe un administrador de seguridad y el cierre de este ExecutorService puede manipular hilos que la persona que llama no puede modificar porque no tiene RuntimePermission ("modifyThread"), o el método checkAccess del administrador de seguridad niega el acceso.

Esto ... es tan vago como se pone. La especificación no dice nada sobre los "subprocesos del sistema" que se realizan durante el ciclo de vida de un ExecutorService y, además, le permite suministrar sus propios subprocesos, lo que es una prueba de que no debe haber "subprocesos del sistema" involucrados al hacer eso. (Como se hizo anteriormente en mi fuente de muestra)

Se siente como si los implementadores de Java SE vieron que es posible que el shutdown genere SecurityException , por lo que fueron como "oh, de acuerdo, agregaré aquí una verificación de seguridad aleatoria para el cumplimiento" ...

La cuestión es que, al leer la fuente de OpenJDK (openjdk-6-src-b20-21_jun_2010), resulta que la única forma en que se crea un subproceso es llamando a la ThreadFactory suministrada (que nunca se llama en mi testcase desde que No preStartAllCoreThreads ningún trabajo y no llamo preStartAllCoreThreads o preStartAllCoreThreads ). Por lo tanto, el control de seguridad se realiza sin motivo aparente en ThreadPoolExecutor de OpenJDK (como se hace en sun-jdk-1.6 pero no tengo la fuente):

/** * 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 se llama antes de hacer nada ...

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

Como puede ver, incondicionalmente invoca checkPermission(shutdownPerm) en el administrador de seguridad .... shutdownPerm se define como ... privado estático final RuntimePermission shutdownPerm = new RuntimePermission ("modifyThread");

... lo que no tiene ningún sentido por lo que puedo decir, porque modifyThread implica acceso a los subprocesos del sistema , y no hay subprocesos del sistema en juego aquí, de hecho, no hay ningún subproceso porque no presenté ningún trabajo o prearran, e incluso si hubiera hilos, serían mis hilos porque pasé en una ThreadFactory . La especificación no dice nada acerca de la muerte mágica, aparte de eso, si los hilos del sistema están involucrados (no lo están), podría haber una excepción SecurityException .

Básicamente, ¿por qué no puedo eliminar la línea que controla el acceso a los subprocesos del sistema? No veo ninguna implicación de seguridad pidiéndolo. ¿Y cómo nadie más se ha topado con este problema? He visto una publicación en un rastreador de problemas donde "resolvieron" este problema cambiando una llamada a shutdownNow a shutdown , obviamente, eso no lo solucionó.


Suena como una implementación perezosa y / o segura. En lugar de verificar si hay otros hilos involucrados, solo se asume que algunos están involucrados. Es mejor lanzar una excepción de seguridad en lugar de dejar un posible agujero de seguridad.