Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AccessControlException is not always thrown in threads from the common java pool

Tags:

java

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.

like image 388
DPM Avatar asked Jun 30 '20 17:06

DPM


People also ask

What is common pool in Java?

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 .

How many threads does ForkJoinPool commonPool have?

(By default, the common pool allows a maximum of 256 spare threads.) Using a value (for example Integer.

What is ThreadPoolExecutor in Java?

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.


1 Answers

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 integer
  • java.util.concurrent.ForkJoinPool.common.threadFactory - the class name of a ForkJoinPool.ForkJoinWorkerThreadFactory
  • java.util.concurrent.ForkJoinPool.common.exceptionHandler - the class name of a Thread.UncaughtExceptionHandler

If 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);
    }
}
like image 173
teppic Avatar answered Oct 27 '22 14:10

teppic