From 87128905fe3b7446775f39d5ca45dfb534c0101d Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Mon, 25 May 2026 12:30:14 +0200 Subject: [PATCH 1/2] ci: add Alpine+JDK21/25 test variants with ZGC for APPSEC-62784 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the CI infrastructure needed to reproduce and verify the SIGSEGV reported in APPSEC-62784. The crash occurs on Alpine (musl libc) with ZGC Generational on JDK 21+ — not on Ubuntu/glibc. - Add ci/alpine-temurin21 and ci/alpine-temurin25 Docker images - Add corresponding x86_64 matrix entries in actions.yml - Enable ZGC flags for JDK 21+ HotSpot test runs in build.gradle so that ReachabilityFenceTest exercises the concurrent-GC window where the stale-pointer crash occurs (stop-the-world GC never exposes it) - Skip ZGC flags for IBM OpenJ9/Semeru (no HotSpot ZGC) - JDK 23+ uses -XX:+UseZGC only; -XX:+ZGenerational was removed by JEP 474 (Generational ZGC became the only mode) --- .github/workflows/actions.yml | 13 +++++++++++++ build.gradle | 13 +++++++++++++ ci/alpine-temurin21/Dockerfile | 8 ++++++++ ci/alpine-temurin25/Dockerfile | 8 ++++++++ 4 files changed, 42 insertions(+) create mode 100644 ci/alpine-temurin21/Dockerfile create mode 100644 ci/alpine-temurin25/Dockerfile diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 679c08c8..7975abe8 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -541,6 +541,19 @@ jobs: arch: x86_64 docker_image: centos6-stock8 test_java_home_var: JAVA_8_HOME + # Alpine (musl libc) + JDK 21/25: the exact environment where APPSEC-62784 + # SIGSEGV crashes were observed in production. These entries verify that + # ReachabilityFenceTest passes on the affected platform/JDK combinations. + - runs-on: ubuntu-24.04 + os: linux + arch: x86_64 + docker_image: alpine-temurin21 + test_java_home_var: JAVA_21_HOME + - runs-on: ubuntu-24.04 + os: linux + arch: x86_64 + docker_image: alpine-temurin25 + test_java_home_var: JAVA_25_HOME - runs-on: arm-4core-linux-ubuntu24.04 os: linux arch: aarch64 diff --git a/build.gradle b/build.gradle index 805260e6..a6059036 100644 --- a/build.gradle +++ b/build.gradle @@ -509,6 +509,19 @@ tasks.withType(Test).configureEach { it.jvmArgs += ['-Xcheck:jni'] } + // On JDK 21+, use ZGC — the concurrent GC that triggered APPSEC-62784. + // This makes ReachabilityFenceTest actually exercise the crash scenario rather than + // relying on a stop-the-world GC that would never expose the stale-pointer window. + // On JDK 21-22, opt into Generational ZGC explicitly (it became the only mode in JDK 23 + // via JEP 474, so -XX:+ZGenerational is obsolete/removed from JDK 23 onwards). + def jvmMajor = it.javaLauncher.get().metadata.languageVersion.asInt() + if (jvmMajor >= 21 && it.javaLauncher.get().metadata.vendor != "IBM") { + it.jvmArgs += ['-XX:+UseZGC'] + if (jvmMajor <= 22) { + it.jvmArgs += ['-XX:+ZGenerational'] + } + } + it.jvmArgs += ['-Dorg.slf4j.simpleLogger.defaultLogLevel=DEBUG', '-DDD_APPSEC_DDWAF_EXIT_ON_LEAK=true'] it.jvmArgs += ['-DuseReleaseBinaries=true'] it.dependsOn copyNativeLibs diff --git a/ci/alpine-temurin21/Dockerfile b/ci/alpine-temurin21/Dockerfile new file mode 100644 index 00000000..eb799cf8 --- /dev/null +++ b/ci/alpine-temurin21/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine:3.19 +# JDK 8 retained for build toolchain compatibility (sourceCompatibility = 1.8) +RUN apk add --no-cache bash openjdk8 +COPY --from=eclipse-temurin:21-jdk-alpine /opt/java/openjdk /usr/lib/jvm/21 +ENV JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk +ENV JAVA_8_HOME=/usr/lib/jvm/java-1.8-openjdk +ENV JAVA_21_HOME=/usr/lib/jvm/21 +ENV PATH=$JAVA_HOME/bin:$PATH diff --git a/ci/alpine-temurin25/Dockerfile b/ci/alpine-temurin25/Dockerfile new file mode 100644 index 00000000..dfc2f77a --- /dev/null +++ b/ci/alpine-temurin25/Dockerfile @@ -0,0 +1,8 @@ +FROM alpine:3.19 +# JDK 8 retained for build toolchain compatibility (sourceCompatibility = 1.8) +RUN apk add --no-cache bash openjdk8 +COPY --from=eclipse-temurin:25-jdk-alpine /opt/java/openjdk /usr/lib/jvm/25 +ENV JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk +ENV JAVA_8_HOME=/usr/lib/jvm/java-1.8-openjdk +ENV JAVA_25_HOME=/usr/lib/jvm/25 +ENV PATH=$JAVA_HOME/bin:$PATH From 8cb55af316c682f843ce301bd8d3805bc2384a3c Mon Sep 17 00:00:00 2001 From: "alejandro.gonzalez" Date: Sat, 6 Jun 2026 16:03:44 +0200 Subject: [PATCH 2/2] Make ZGC a matrix parameter instead of unconditional for JDK 21+ Add use_zgc: true to Alpine+JDK21/25 matrix entries and pass -PuseZGC to Gradle only for those variants. This lets ubuntu2204-temurin21 run with the default GC while alpine-temurin21/25 run with ZGC, giving coverage of both configurations. Addresses review feedback on PR #201. --- .github/workflows/actions.yml | 9 +++++---- build.gradle | 8 +++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index c6eb8860..339ce47a 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -535,19 +535,20 @@ jobs: arch: x86_64 docker_image: centos6-stock8 test_java_home_var: JAVA_8_HOME - # Alpine (musl libc) + JDK 21/25: the exact environment where APPSEC-62784 - # SIGSEGV crashes were observed in production. These entries verify that - # ReachabilityFenceTest passes on the affected platform/JDK combinations. + # Alpine (musl libc) + JDK 21/25 with ZGC: the exact environment where + # APPSEC-62784 SIGSEGV crashes were observed in production. - runs-on: ubuntu-24.04 os: linux arch: x86_64 docker_image: alpine-temurin21 test_java_home_var: JAVA_21_HOME + use_zgc: true - runs-on: ubuntu-24.04 os: linux arch: x86_64 docker_image: alpine-temurin25 test_java_home_var: JAVA_25_HOME + use_zgc: true - runs-on: arm-4core-linux-ubuntu24.04 os: linux arch: aarch64 @@ -613,7 +614,7 @@ jobs: - name: Run tests (docker) run: | docker run --rm -w $(pwd) -v $(pwd):$(pwd) ${{ matrix.docker_image }} \ - sh -c './gradlew check --no-daemon --info -Prelease -PuseReleaseBinaries -Dorg.gradle.native=false -PtestJavaHome="$${{ matrix.test_java_home_var }}"' + sh -c './gradlew check --no-daemon --info -Prelease -PuseReleaseBinaries -Dorg.gradle.native=false -PtestJavaHome="$${{ matrix.test_java_home_var }}"${{ matrix.use_zgc && ' -PuseZGC' || '' }}' if: ${{ matrix.os == 'linux' }} - name: Run tests (no docker) run: | diff --git a/build.gradle b/build.gradle index a6059036..64342989 100644 --- a/build.gradle +++ b/build.gradle @@ -509,13 +509,11 @@ tasks.withType(Test).configureEach { it.jvmArgs += ['-Xcheck:jni'] } - // On JDK 21+, use ZGC — the concurrent GC that triggered APPSEC-62784. - // This makes ReachabilityFenceTest actually exercise the crash scenario rather than - // relying on a stop-the-world GC that would never expose the stale-pointer window. + // When -PuseZGC is passed, force ZGC to match the production GC that triggered APPSEC-62784. // On JDK 21-22, opt into Generational ZGC explicitly (it became the only mode in JDK 23 // via JEP 474, so -XX:+ZGenerational is obsolete/removed from JDK 23 onwards). - def jvmMajor = it.javaLauncher.get().metadata.languageVersion.asInt() - if (jvmMajor >= 21 && it.javaLauncher.get().metadata.vendor != "IBM") { + if (project.hasProperty('useZGC')) { + def jvmMajor = it.javaLauncher.get().metadata.languageVersion.asInt() it.jvmArgs += ['-XX:+UseZGC'] if (jvmMajor <= 22) { it.jvmArgs += ['-XX:+ZGenerational']