0

I have a simple java application ClientApp which has Java Security Manager enable. This application is trying to call method of Test jar which fetches "os.arch" System property. Since it is time consuming we are invoking a new thread using Completable task.

This gives exception

java.util.concurrent.ExecutionException: java.security.AccessControlException: access denied ("java.util.PropertyPermission" "os.arch" "read")
    at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)
    at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)
    at SecurityApplication.ClientApp.main(ClientApp.java:23)
Caused by: java.security.AccessControlException: access denied ("java.util.PropertyPermission" "os.arch" "read")
    at java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
    at java.security.AccessController.checkPermission(AccessController.java:884)
    at java.lang.SecurityManager.checkPermission(SecurityManager.java:549)
    at java.lang.SecurityManager.checkPropertyAccess(SecurityManager.java:1294)
    at java.lang.System.getProperty(System.java:717)
    at com.ravindra.CustomSupplier.get(CustomSupplier.java:10)
    at com.ravindra.CustomSupplier.get(CustomSupplier.java:5)
    at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
    at java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1582)
    at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
    at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
    at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

ClientApp :

import com.test.App;

public class ClientApp
{
    public static void main(String[] args)
    {
        //Enable security 
        SecurityManager securityManager = new SecurityManager();
        System.setSecurityManager(securityManager);

        TestApp app = new TestApp();

        Future<String> future = app.getOsArchitecture();

        try
        {
            // blocking Aysnc get Future call for result
            System.out.println(future.get());
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }
}

Test.jar has below 2 class file

Test.java

final public class TestApp
{
    public Future<String> getOsArchitecture()
    {
        CompletableFuture<String> completableFuture
                = CompletableFuture.supplyAsync(new CustomSupplier());

        return completableFuture;
    }
}

CustomSupplier.java

public class CustomSupplier implements Supplier<String> {
    public CustomSupplier() {
    }

    public String get() {
        //Time consuming complex Task and finally return "os.arch"
        //...
        //...
        //...
        return System.getProperty("os.arch");
    }
}

However, when we disable the Java Security Manager, the program runs correctly.

Any help is kindly appreciated.

Pankaj007
  • 13
  • 7
  • 1
    `System.getProperty(String)` bears a single hash lookup. That’s definitely not time consuming. Even if it was, performing the action asynchronously but waiting for the result immediately after submitting, only raised the required time a bit. – Holger Feb 25 '20 at 08:35
  • Time Consuming task means business logic related to my project – Pankaj007 Feb 25 '20 at 10:37
  • 1
    So you’re performing a time consuming asynchronous task which does not depend on the value of `System.getProperty("os.arch")` and at the end you’re returning the value of `System.getProperty("os.arch")` as result, which has no relationship to what the asynchronous task did? – Holger Feb 25 '20 at 11:50
  • yes, your are correct – Pankaj007 Feb 27 '20 at 05:04
  • Hi All, We have resolved the issue. The API was written in such a way that we needed to pass the our own Executor Service so that it doesn't use it ForkJoin common Pool for creating thread. When we create our own Executor Service, it used the App context so it has the permission to fetch the System property – Pankaj007 Feb 27 '20 at 05:17

1 Answers1

1

The default ForkJoinPool uses threads which are configured to have no privileges.

One solution is to create a new ForkJoinPool instance with a custom thread factory or any other Executor implementation using ordinary threads. The catch is that creating your own thread pool requires related permissions to be granted by the security manager.

An alternative is to execute the query as privileged action ignoring the limitations of the calling thread:

public class CustomSupplier implements Supplier<String> {
    public CustomSupplier() {
    }

    public String get() {
        //Time consuming complex Task and finally return "os.arch"
        //...
        //...
        //...
        return AccessController.doPrivileged(
            (PrivilegedAction<String>)() -> System.getProperty("os.arch"));
    }
}

This ignores the privileges of the caller and executes the query with the permission of the code calling doPrivileged, i.e. your CustomSupplier. By default, application code is granted the right to read the "os.arch" system property.

But since this system property is not expected to ever change during the application’s lifetime and querying it is not an expensive operation, there is no need to perform the query in the background.

public class CustomSupplier implements Supplier<String> {
    public CustomSupplier() {
    }

    public String get() {
        //Time consuming complex Task and finally return "os.arch"
        //...
        //...
        //...
        return ARCH;
    }
    static final String ARCH = System.getProperty("os.arch");
}
Holger
  • 285,553
  • 42
  • 434
  • 765