I have the following program:
public static void main(String[] args) throws Exception{
ForkJoinTask<?> read = ForkJoinPool.commonPool().submit(new Runnable() {
@Override
public void run() {
SecurityManager appsm = System.getSecurityManager();
if (appsm != null) {
appsm.checkPermission(new PropertyPermission("os.arch", "read"));
System.out.println("Permissions are OK!!!!");
} else {
System.out.println("No system mangerrr :(");
}
}
});
read.get();
System.out.println("End of the program!");
}
I also set this system property:
-Djava.security.manager
My question is the following - why sometimes I get "End of the program!" written to the standard output, but sometimes I get Exception in thread "main" java.util.concurrent.ExecutionException: java.security.AccessControlException: access denied ("java.util.PropertyPermission" "os.arch" "read")
?
Shouldn't it always throw me this exception since the common pool's threads are created without any permissions? Shouldn't this behavior be always the same and do not vary throughout the different executions?
I am running this from my IntelliJ IDE using Zulu OPEN JDK 8.0.252.
ForkJoinPool#commonPool() is a static thread-pool, which is lazily initialized when is actually needed. Two major concepts use the commonPool inside JDK: CompletableFuture and Parallel Streams .
(By default, the common pool allows a maximum of 256 spare threads.) Using a value (for example Integer.
ThreadPoolExecutor is an ExecutorService to execute each submitted task using one of possibly several pooled threads, normally configured using Executors factory methods. It also provides various utility methods to check current threads statistics and control them.
This is happening because tasks submitted to a ForkJoinPool
may execute in the calling thread on join
. In your case this happens when you call read.get()
; most of the time a worker from the common pool picks up the task and executes it (and the security check fails), but sometimes the task runs in the calling thread (and the security check passes).
You can confirm this by logging the thread name.
public void run() {
SecurityManager appsm = System.getSecurityManager();
if (appsm != null) {
System.err.println("Thread: " + Thread.currentThread().getName());
appsm.checkPermission(new PropertyPermission("os.arch", "read"));
System.out.println("Permissions are OK!!!!");
} else {
System.out.println("No system mangerrr :(");
}
}
This will log Thread: ForkJoinPool.commonPool-worker-1
when the security check fails and Thread: main
when the security check passes.
The difference in behaviour between the two cases occurs because your main thread has os.arch
read
permission, but the threads in your common pool have no permissions.
From the ForkJoinPool
javadoc:
The common pool is by default constructed with default parameters, but these may be controlled by setting three system properties:
java.util.concurrent.ForkJoinPool.common.parallelism
- the parallelism level, a non-negative integerjava.util.concurrent.ForkJoinPool.common.threadFactory
- the class name of a ForkJoinPool.ForkJoinWorkerThreadFactoryjava.util.concurrent.ForkJoinPool.common.exceptionHandler
- the class name of a Thread.UncaughtExceptionHandlerIf a SecurityManager is present and no factory is specified, then the default pool uses a factory supplying threads that have no Permissions enabled.
The default security in the common pool is very conservative. It runs tasks with no privileges and cleans up ThreadLocal
vars after task completion to prevent information leakage.
The common pool security has to be conservative because it's available to all code—if it defaulted to running under the main SecurityManager
then any code you tried to sandbox further could escalate privileges by running tasks in the common pool.
If you wan't to allow code to run tasks in the common pool with the privileges allowed by the SecurityManager
that you've configured on the command line then you'll have to set the java.util.concurrent.ForkJoinPool.common.threadFactory
to an implementation you provide, e.g.
public final class CustomForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory {
@Override
public ForkJoinWorkerThread newThread(ForkJoinPool pool) {
return ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With