Skip to content

Common ForkJoinPool uses InnocuousForkJoinWorkerThread, breaking setContextClassLoader #96

@shultseva

Description

@shultseva

Describe the bug

When running the application inside a Docker container with param -Djava.security.manager=allow, the Java common ForkJoinPool creates instances of InnocuousForkJoinWorkerThread instead of the standard ForkJoinWorkerThread. As a result, the Thread.setContextClassLoader throws the SecurityException.
This behavior differs from running the same application outside of Docker, where normal ForkJoinWorkerThread instances are created, and no exception is thrown.
Reproduced on 21.0.6.7-1, but does not reproduce on 21.0.5.11-1

To Reproduce

Docker:
taken from https://github.com/corretto/corretto-docker/blob/main/21/jdk/al2-generic/Dockerfile

FROM amazonlinux:2

# success:
# ARG version=21.0.5.11-1

# failure:
ARG version=21.0.6.7-1

# In addition to installing the Amazon corretto, we also install
# fontconfig. The folks who manage the docker hub's
# official image library have found that font management
# is a common usecase, and painpoint, and have
# recommended that Java images include font support.
#
# See:
#  https://github.com/docker-library/official-images/blob/master/test/tests/java-uimanager-font/container.java

# The logic and code related to Fingerprint is contributed by @tianon in a Github PR's Conversation
# Comment = https://github.com/docker-library/official-images/pull/7459#issuecomment-592242757
# PR = https://github.com/docker-library/official-images/pull/7459
RUN set -eux \
    && export GNUPGHOME="$(mktemp -d)" \
    && curl -fL -o corretto.key https://yum.corretto.aws/corretto.key \
    && gpg --batch --import corretto.key \
    && gpg --batch --export --armor '6DC3636DAE534049C8B94623A122542AB04F24E3' > corretto.key \
    && rpm --import corretto.key \
    && rm -r "$GNUPGHOME" corretto.key \
    && curl -fL -o /etc/yum.repos.d/corretto.repo https://yum.corretto.aws/corretto.repo \
    && grep -q '^gpgcheck=1' /etc/yum.repos.d/corretto.repo \
    && echo "priority=9" >> /etc/yum.repos.d/corretto.repo \
    && yum install -y java-21-amazon-corretto-devel-$version \
    && (find /usr/lib/jvm/java-21-amazon-corretto -name src.zip -delete || true) \
    && yum install -y fontconfig \
    && yum clean all

ENV LANG=C.UTF-8
ENV JAVA_HOME=/usr/lib/jvm/java-21-amazon-corretto


WORKDIR /app

COPY App.java .

RUN javac App.java

CMD ["java", "-Djava.security.manager=allow", "App"]

App:

public class App {
    public static void main(String[] args) {
        var defaultExecutor = ForkJoinPool.commonPool();
        defaultExecutor.execute(() -> {
            Thread t = Thread.currentThread();
            System.out.println("Running in thread: " + t.getClass().getName());

            try {
                t.setContextClassLoader(new java.net.URLClassLoader(new java.net.URL[0]));
                System.out.println("Set context class loader.");
            } catch (SecurityException e) {
                System.err.println("SecurityException: " + e.getMessage());
                e.printStackTrace();
            }
        });

        try {
            Thread.sleep(1_000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

Build:

#!/bin/bash

cp ../src/main/java/App.java .

docker build -t demo .
docker run --rm demo

Result:

Running in thread: java.util.concurrent.ForkJoinWorkerThread$InnocuousForkJoinWorkerThread
SecurityException: setContextClassLoader
java.lang.SecurityException: setContextClassLoader
        at java.base/java.util.concurrent.ForkJoinWorkerThread$InnocuousForkJoinWorkerThread.setContextClassLoader(ForkJoinWorkerThread.java:231)
        at App.lambda$main$0(App.java:11)
        at java.base/java.util.concurrent.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1423)
        at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387)
        at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312)
        at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843)
        at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808)
        at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188)

Expected behavior

When running inside Docker, the common ForkJoinPool should create instances of java.util.concurrent.ForkJoinWorkerThread instead of InnocuousForkJoinWorkerThread, and no security exceptions should be thrown.

Alternatively, a clear explanation of why the jdk creates InnocuousForkJoinWorkerThread in this context, and how this behavior can be avoided.

Platform information

OS: macOs, 14.4.1
Version: Corretto-21.0.6.7-1

Additional context

The project does not use a Security Manager.

Running the same application either inside a container with Corretto 21.0.5 or locally (outside a container) produces the following result:

Running in thread: java.util.concurrent.ForkJoinWorkerThread
Set context class loader.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions