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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions