diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/background.md b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/background.md deleted file mode 100644 index bbfd967adb..0000000000 --- a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/background.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: Understand Azure Cobalt 100, OpenJDK, and Armv9 PAC/BTI - -weight: 2 - -layout: "learningpathall" ---- - -## Azure Cobalt 100 Arm-based processor - -Azure Cobalt 100 is Microsoft's first-generation Arm-based processor, built on Arm Neoverse N2. It allocates a dedicated physical core for each vCPU, which provides consistent and predictable performance for cloud-native, scale-out Linux workloads. These characteristics make Cobalt 100 well suited for Java application servers and security-sensitive workloads where PAC/BTI hardware support is available and beneficial. - -To learn more, see the Microsoft blog [Announcing the preview of new Azure VMs based on the Azure Cobalt 100 processor](https://techcommunity.microsoft.com/blog/azurecompute/announcing-the-preview-of-new-azure-vms-based-on-the-azure-cobalt-100-processor/4146353). - -## OpenJDK and Armv9 PAC/BTI security features - -OpenJDK is the open-source reference implementation of Java Platform, Standard Edition (Java SE). It provides the core compiler, runtime, and class libraries you use to build and run Java applications across operating systems and hardware platforms. OpenJDK forms the basis for most modern Java distributions, while different vendors package and support their own builds. - -Armv9 Pointer Authentication (PAC) and Branch Target Identification (BTI) are security features designed to make control-flow attacks harder. PAC helps protect return addresses and pointers by adding a cryptographic signature that is checked before the pointer is used, which can detect tampering such as return-oriented programming attempts. BTI complements this by restricting where indirect branches are allowed to land, helping prevent attackers from jumping into unintended instruction sequences. Together, PAC and BTI strengthen software defenses at the instruction-set level, especially for modern operating systems, hypervisors, and applications that need improved resistance to memory-corruption exploits. When OpenJDK is compiled with branch protection enabled, the JVM binary and its native libraries are built with these protections, extending the security benefit to Java workloads running on the platform. - -## What you've learned and what's next - -You now have the background on Azure Cobalt 100, OpenJDK, and Armv9 PAC/BTI features. - -Next, you'll create an Arm-based Azure VM, build OpenJDK with branch protection, and validate PAC/BTI readiness in the installed JVM. diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/final-vm.png b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/final-vm.png deleted file mode 100644 index 5207abfb41..0000000000 Binary files a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/final-vm.png and /dev/null differ diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/instance.png b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/instance.png deleted file mode 100644 index 285cd764a5..0000000000 Binary files a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/instance.png and /dev/null differ diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/instance1.png b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/instance1.png deleted file mode 100644 index b9d22c352d..0000000000 Binary files a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/instance1.png and /dev/null differ diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/instance4.png b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/instance4.png deleted file mode 100644 index 2a0ff1e3b0..0000000000 Binary files a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/instance4.png and /dev/null differ diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/ubuntu-pro.png b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/ubuntu-pro.png deleted file mode 100644 index d54bd75ca6..0000000000 Binary files a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/images/ubuntu-pro.png and /dev/null differ diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/instance.md b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/instance.md deleted file mode 100644 index 805db11e9d..0000000000 --- a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/instance.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Create an Azure Cobalt 100 Arm64 virtual machine -weight: 3 - -### FIXED, DO NOT MODIFY -layout: learningpathall ---- - -## Create an Arm-based Azure virtual machine - -There are several common ways to create an Arm-based Cobalt 100 virtual machine, and you can choose the method that best fits your workflow or requirements: the Azure Portal, the Azure CLI, or an infrastructure as code (IaC) tool. This Learning Path uses the Azure Portal. - -This Learning Path focuses on general-purpose virtual machines in the Dpsv6 series. For more information, see the [Microsoft Azure guide for the Dpsv6 size series](https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/general-purpose/dpsv6-series). - -While the steps to create this instance are included here for convenience, you can also refer to the [Deploy a Cobalt 100 virtual machine on Azure Learning Path](/learning-paths/servers-and-cloud-computing/cobalt/). - -Creating a virtual machine based on Azure Cobalt 100 is no different from creating any other virtual machine in Azure. To create an Azure virtual machine: - -- Launch the Azure portal and navigate to **Virtual Machines**. -- Select **Create**, and select **Virtual Machine** from the drop-down list. -- Inside the **Basic** tab, fill in the instance details such as **Virtual machine name** and **Region**. -- Select the image for your virtual machine (for example, Ubuntu Pro 24.04 LTS) and select **Arm64** as the VM architecture. -- In the **Size** field, select **See all sizes** and select the D-Series v6 family of virtual machines. -- Select **D4ps_v6** from the list as shown in the diagram below: - -![Azure Portal showing D-Series v6 VM size selection with D4ps_v6 highlighted#center](images/instance.png "Select D4ps_v6 from the D-Series v6 family") - -- For **Authentication type**, select **SSH public key**. - -{{% notice Note %}} -Azure can generate an SSH key pair for you and lets you save it for future use. -{{% /notice %}} - -- Fill in the **Administrator username** for your VM. -- Select **Generate new key pair**, and select **RSA SSH Format** as the SSH Key Type. - -{{% notice Note %}} -RSA offers better security with keys longer than 3072 bits. -{{% /notice %}} - -- Give your SSH key a key pair name. -- In the **Inbound port rules**, select **SSH (22)** as the inbound port. - -{{% notice Security %}} -Azure sets the source to **Any** by default, which allows SSH connections from the entire internet. After creating the VM, update the SSH inbound rule in the Network Security Group to restrict the source to your own IP address. -{{% /notice %}} - -![Azure Portal inbound port rules configuration showing SSH (22) selected as the allowed incoming port#center](images/instance1.png "Configure inbound port rules for SSH access") - -- Now select the **Review + Create** tab and review the configuration for your virtual machine. It should look like the following: - -![Azure Portal Review + Create tab showing VM configuration summary ready for deployment#center](images/ubuntu-pro.png "Review VM configuration before creation") - -- When you're happy with your selection, select the **Create** button and then **Download Private key and Create Resource** button. - -![Azure Portal showing Create button and SSH key download dialog#center](images/instance4.png "Download SSH key and create the virtual machine") - -Your virtual machine should be ready and running in a few minutes. You can SSH into the virtual machine using the private key, along with the public IP details. - -![Azure Portal showing successful VM deployment with confirmation details#center](images/final-vm.png "Successful VM deployment confirmation") - -{{% notice Note %}}To learn more about Arm-based virtual machines in Azure, see "Getting Started with Microsoft Azure" in [Get started with Arm-based cloud instances](/learning-paths/servers-and-cloud-computing/csp/azure/).{{% /notice %}} - -## What you've learned and what's next - -You've created an Azure Cobalt 100 Arm-based virtual machine running Ubuntu 24.04 LTS with SSH authentication configured. The virtual machine is now ready for compiling and installing OpenJDK with PAC/BTI support. - -Next, you'll download and compile OpenJDK on the VM, register the resulting JVM runtime, and run validation checks for PAC/BTI support. diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/jvm-compile.md b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/jvm-compile.md deleted file mode 100644 index 32ef8442b3..0000000000 --- a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/jvm-compile.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: Compile OpenJDK from source -weight: 4 - -### FIXED, DO NOT MODIFY -layout: learningpathall ---- - -## Install build prerequisites - -Install these prerequisites in a terminal on your Cobalt 100 VM. Also install a bootstrap JVM to build the PAC/BTI-enabled JVM: - -```bash -sudo apt update -sudo apt install -y zip git build-essential wget curl autoconf -sudo apt install -y libcups2-dev libasound2-dev libfontconfig1-dev alsa-ucm-conf ubuntu-drivers-common -sudo apt install -y libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev -sudo apt install -y openjdk-25-jdk -``` - -## Download the OpenJDK source - -Download OpenJDK. Since the bootstrap JVM is v25, build v26 with PAC/BTI support enabled: - -```bash -cd $HOME -git clone https://github.com/openjdk/jdk -cd jdk -git checkout jdk-26+35 -``` - -## Configure the OpenJDK source build - -Configure the OpenJDK source build and enable branch protection support: - -```bash -bash configure --enable-branch-protection -``` - -## Build the JVM - -Run the build to create the JVM. This process can take more than 30 minutes: - -```bash -make images -``` - -## Register the JVM with the system - -Run the following commands to register the newly created JDK/JVM with the system: - -```bash -cd $HOME/jdk -WD=$(pwd)/build/linux-aarch64-server-release -cd /usr/lib/jvm -sudo ln -s ${WD}/jdk ./java-26-openjdk-arm64 -sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/java-26-openjdk-arm64/bin/java 3000 -cd -``` - -Confirm the newly installed JVM: - -```bash -java --version -``` - -Output should be similar to: - -```output -openjdk 26-internal 2026-03-17 -OpenJDK Runtime Environment (build 26-internal-adhoc.ubuntu.jdk) -OpenJDK 64-Bit Server VM (build 26-internal-adhoc.ubuntu.jdk, mixed mode) -``` - -## What you've learned and what's next - -You've compiled OpenJDK from source with branch protection enabled, registered the resulting JVM with the system, and confirmed the installation with `java --version`. - -Next, you'll run a test script to verify that PAC/BTI is active in the JVM you just built. diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/jvm-testing.md b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/jvm-testing.md deleted file mode 100644 index 6586799da4..0000000000 --- a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/jvm-testing.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Test the installed JVM for PAC/BTI enablement -weight: 5 - -### FIXED, DO NOT MODIFY -layout: learningpathall ---- - - -## Set up the test script - -Clone this repo and set execute permissions for the downloaded test script: - -```bash -git clone https://github.com/DougAnsonAustinTx/pac-bti-jdk-assets -cd pac-bti-jdk-assets -chmod 755 ./test-pacbti.sh -``` - -## Run the test script - -Run the test script to confirm PAC/BTI enablement: - -```bash -./test-pacbti.sh --java /usr/bin/java -``` - -Output should resemble: - -```output -== JVM PAC/BTI check == -java executable : /home/ubuntu/jdk/build/linux-aarch64-server-release/jdk/bin/java -libjvm : /home/ubuntu/jdk/build/linux-aarch64-server-release/jdk/lib/server/libjvm.so - --- Host support (auxv/hwcaps) -- -PAC APIA/generic support : yes (PACA=yes PACG=yes) -BTI support : yes - --- JVM support/config -- -UseBranchProtection flag : yes -JVM default flag value : {default} - --- Binary instruction scan -- -java contains PAC instr : yes -java contains BTI instr : yes -libjvm contains PAC instr: no -libjvm contains BTI instr: no - --- Verdict -- -PAC status : possible-but-not-proven -BTI status : yes - -Interpretation: - PAC-capable build detected, but no running JVM cmdline was checked. - BTI is likely enabled in the runtime binaries. -``` - -## What you've learned - -Some OpenJDK builds are not distributed with PAC/BTI enabled by default because they must remain compatible with older Arm platforms. When you need these protections, you can build and register your own JVM with branch protection support. - -When a PAC/BTI-enabled JVM runs on a platform that supports these features, Java workloads gain additional control-flow protection. diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/_index.md b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/_index.md similarity index 51% rename from content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/_index.md rename to content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/_index.md index 5d507486d0..cf46a6b415 100644 --- a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/_index.md +++ b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/_index.md @@ -1,26 +1,23 @@ --- -title: Build OpenJDK with PAC/BTI support on Azure Cobalt 100 +title: Verify OpenJDK PAC/BTI using a Google Cloud C4A VM -description: Learn how to compile OpenJDK with branch protection on an Azure Cobalt 100 Arm VM and verify PAC/BTI support in the resulting JVM. +description: Learn how to verify PAC/BTI support in OpenJDK on a Google Cloud C4A Arm-based VM and interpret the JVM readiness result. draft: true cascade: draft: true -minutes_to_complete: 60 +minutes_to_complete: 30 -who_is_this_for: This is an introductory topic for Java developers who want to compile OpenJDK from source with PAC/BTI branch protection enabled and verify the resulting JVM on an Azure Cobalt 100 Arm-based virtual machine. +who_is_this_for: This Learning Path is for developers who want to validate OpenJDK PAC/BTI support on Google Cloud C4A Arm-based virtual machines. learning_objectives: - - Provision an Azure Cobalt 100 Arm-based virtual machine with Ubuntu Pro 24.04 LTS. - - Build OpenJDK on Arm with branch protection support enabled. + - Provision a Google Cloud C4A Arm-based virtual machine with SUSE Linux Enterprise Server. + - Install OpenJDK on the Arm-based VM. - Verify PAC/BTI readiness in the installed JVM runtime. prerequisites: - - A [Microsoft Azure](https://azure.microsoft.com/) account with access to Cobalt 100 based instances (Dpsv6) - - Familiarity with the Linux command line and SSH - - Basic familiarity with Java and the JDK toolchain - + - A [Google Cloud Platform (GCP)](https://cloud.google.com/free) account with billing enabled author: Doug Anson @@ -28,7 +25,7 @@ author: Doug Anson skilllevels: Introductory subjects: Performance and Architecture cloud_service_providers: - - Microsoft Azure + - Google Cloud armips: - Neoverse @@ -43,8 +40,8 @@ operatingsystems: further_reading: - resource: - title: Azure Virtual Machines documentation - link: https://learn.microsoft.com/en-us/azure/virtual-machines/ + title: Google Cloud documentation + link: https://cloud.google.com/docs type: documentation - resource: title: OpenJDK build documentation @@ -55,8 +52,8 @@ further_reading: link: https://github.com/openjdk/jdk type: documentation - resource: - title: Learn the architecture - Providing protection for complex software - link: https://developer.arm.com/documentation/102433/latest/ + title: Arm "Arm64" machine instruction reference + link: https://developer.arm.com/documentation/100076/latest/ type: documentation @@ -65,4 +62,4 @@ further_reading: weight: 1 # _index.md always has weight of 1 to order correctly layout: "learningpathall" # All files under learning paths have this same wrapper learning_path_main_page: "yes" # This should be surfaced when looking for related content. Only set for _index.md of learning path content. ---- +--- \ No newline at end of file diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/_next-steps.md b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/_next-steps.md similarity index 100% rename from content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-azure/_next-steps.md rename to content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/_next-steps.md diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/background.md b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/background.md new file mode 100644 index 0000000000..e495e68e1f --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/background.md @@ -0,0 +1,27 @@ +--- +title: Understand Google Cloud C4A Axion processors, OpenJDK, and Armv9 PAC/BTI + +weight: 2 + +layout: "learningpathall" +--- + +## Explore Google Cloud C4A instances + +Google Axion C4A is a family of Arm-based VMs built on Google's custom Axion processors, which use Arm Neoverse-V2 cores. These VMs deliver high performance with improved energy efficiency for modern cloud workloads such as CI/CD pipelines, microservices, media processing, and general-purpose applications. + +The C4A series provides an Arm-based alternative to x86 VMs, enabling developers to evaluate cost, performance, and efficiency trade-offs in Google Cloud. For Kubernetes users, C4A instances provide a practical way to run Arm-native clusters and validate tooling such as Helm on modern cloud infrastructure. + +To learn more about Google Axion, see the Google blog [Introducing Google Axion Processors, our new Arm-based CPUs](https://cloud.google.com/blog/products/compute/introducing-googles-new-arm-based-cpu). + +## OpenJDK and the PAC/BTI Arm v9 instructions + +OpenJDK is the open-source reference implementation of Java Platform, Standard Edition (Java SE). It provides the core compiler, runtime, and class libraries you use to build and run Java applications across operating systems and hardware platforms. OpenJDK forms the basis for most modern Java distributions, while different vendors package and support their own builds. + +Armv9 Pointer Authentication (PAC) and Branch Target Identification (BTI) are security features designed to make control-flow attacks harder. PAC helps protect return addresses and pointers by adding a cryptographic signature that is checked before the pointer is used, which can detect tampering such as return-oriented programming attempts. BTI complements this by restricting where indirect branches are allowed to land, helping prevent attackers from jumping into unintended instruction sequences. Together, PAC and BTI strengthen software defenses at the instruction-set level, especially for modern operating systems, hypervisors, and applications that need improved resistance to memory-corruption exploits. + +## What you've learned and what's next + +You now have the background on Google Cloud C4A, OpenJDK, and Armv9 PAC/BTI features. + +Next, you'll create an Arm-based Google Cloud VM, install OpenJDK, and validate PAC/BTI readiness in the installed JVM. diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/images/gcp-shell.png b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/images/gcp-shell.png new file mode 100644 index 0000000000..7e2fc3d1b5 Binary files /dev/null and b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/images/gcp-shell.png differ diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/images/gcp-ssh.png b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/images/gcp-ssh.png new file mode 100644 index 0000000000..597ccd7fea Binary files /dev/null and b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/images/gcp-ssh.png differ diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/images/gcp-vm.png b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/images/gcp-vm.png new file mode 100644 index 0000000000..0d1072e20d Binary files /dev/null and b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/images/gcp-vm.png differ diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/instance.md b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/instance.md new file mode 100644 index 0000000000..58939f1510 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/instance.md @@ -0,0 +1,47 @@ +--- +title: Create a Google Axion C4A virtual machine on Google Cloud +weight: 3 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Overview + +In this section, you provision a Google Axion C4A VM on Google Cloud Platform (GCP) using the `c4a-standard-4` machine type, which provides 4 vCPUs and 16 GB of memory. + +{{% notice Note %}} +For general guidance on setting up a Google Cloud account and project, see the Learning Path [Getting started with Google Cloud Platform](/learning-paths/servers-and-cloud-computing/csp/google/). +{{% /notice %}} + +## Provision a Google Axion C4A VM in the Google Cloud Console + +To create a VM using the C4A instance type: + +- Open the [Google Cloud Console](https://console.cloud.google.com/). +- Go to **Compute Engine** > **VM instances**, and then select **Create instance**. +- Under **Machine configuration**: + - Specify an **Instance name**, **Region**, and **Zone**. + - Set **Series** to **C4A**. + - Select **c4a-standard-4** as the machine type. + +![Google Cloud Console VM creation page showing the C4A series and c4a-standard-4 machine type selection. Use this screen to confirm you are creating an Arm-based C4A instance.#center](images/gcp-vm.png "Creating a Google Axion C4A VM in the Google Cloud Console") + +- Under **OS and storage**, select **Change**, and then choose an Arm64-based operating system image. + - For this Learning Path, select **SUSE Linux Enterprise Server**. + - For the license type, choose **Pay as you go**. + - Select **Select** to apply the changes. +- Under **Networking**, enable **Allow HTTP traffic** and **Allow HTTPS traffic**. +- Select **Create** to launch the virtual machine. + +After the instance starts, click **SSH** next to the VM in the instance list to open a browser-based terminal session. + +![Google Cloud Console VM instances list showing the SSH button for a running C4A instance. Use SSH to open a terminal session on the new VM.#center](images/gcp-ssh.png "Connecting to a running C4A VM using SSH") + +A new browser window opens with a terminal connected to your VM. + +![Browser-based terminal window showing a command prompt on a SUSE Linux VM running on Google Axion C4A. Use this terminal to install OpenJDK and run the PAC/BTI validation script.#center](images/gcp-shell.png "Terminal session connected to the VM") + +## What you've learned and what's next + +In this section, you created a Google Cloud C4A virtual machine and opened an SSH session to the VM. Next, you will install OpenJDK and prepare the PAC/BTI validation script. Next, install the OpenJDK JVM on your VM. diff --git a/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/jvm-testing.md b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/jvm-testing.md new file mode 100644 index 0000000000..151e1fe914 --- /dev/null +++ b/content/learning-paths/servers-and-cloud-computing/openjdk-pacbti-gcp/jvm-testing.md @@ -0,0 +1,391 @@ +--- +title: Install and test the OpenJDK JVM for PAC/BTI enablement +weight: 4 + +### FIXED, DO NOT MODIFY +layout: learningpathall +--- + +## Install the default OpenJDK JVM + +Install the default OpenJDK Java VM: + +```bash +sudo zypper refresh +sudo zypper install -y java +``` + +Confirm the newly installed JVM: + +```bash +java --version +``` + +Output should be similar to: + +```output +openjdk 17.0.13 2024-10-15 +OpenJDK Runtime Environment (build 17.0.13+11-suse-150400.3.48.2-aarch64) +OpenJDK 64-Bit Server VM (build 17.0.13+11-suse-150400.3.48.2-aarch64, mixed mode, sharing) +``` + +Next, download and run a script to confirm PAC/BTI readiness in the JVM you just installed. + +## Set up the test script + +Copy and paste the following script into your C4A SSH session. Save it as `test-pacbti.sh`: + +```bash +#!/usr/bin/env bash +# +# test-pacbti.sh +# +# Purpose: +# Exercise an OpenJDK JVM on Linux/AArch64 and determine whether the +# JVM process is running on a platform that exposes Arm Pointer +# Authentication Code features and Branch Target Identification to +# userspace. +# +# Expected results: +# - Armv9 platform with PAC + BTI exposed: +# FINAL RESULT: POSITIVE +# +# - Armv8 platform lacking PAC + BTI: +# FINAL RESULT: NEGATIVE +# +# - Non-AArch64, missing /proc/self/auxv, or partial exposure: +# FINAL RESULT: INCONCLUSIVE +# +# What this tests: +# This script does not merely inspect the host from the shell. It launches +# a Java probe inside the target JVM. The Java process reads its own +# /proc/self/auxv, so the HWCAP/HWCAP2 values are those visible to the JVM. +# +# Notes: +# - PAC and BTI are architectural/security features. On Linux/AArch64, +# userspace should discover them through AT_HWCAP / AT_HWCAP2. +# - This intentionally avoids "try the instruction and catch SIGILL" style +# tests, because Linux kernel documentation says feature probing should +# use hwcaps. +# - Some Armv8.x systems may expose PAC and/or BTI. The solid negative +# requested here applies to Armv8 systems that lack PAC/BTI. +# +# Requirements: +# - Linux +# - AArch64 target JVM for meaningful PAC/BTI result +# - OpenJDK 11+ for Java source-file mode, or javac available as fallback +# +# Usage: +# chmod +x test-pacbti.sh +# ./test-pacbti.sh +# +# Optional: +# JAVA=/path/to/java ./test-pacbti.sh + +set -u + +JAVA_BIN="${JAVA:-java}" + +tmpdir="$(mktemp -d)" +cleanup() { + rm -rf "${tmpdir}" +} +trap cleanup EXIT + +cat > "${tmpdir}/JvmAarch64PacBtiProbe.java" <<'JAVA' +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Locale; +import java.util.Map; +import java.util.TreeMap; + +public final class JvmAarch64PacBtiProbe { + /* + * Linux auxv constants. + * + * AT_HWCAP = 16 + * AT_HWCAP2 = 26 + * + * Linux AArch64 hwcap bit assignments: + * HWCAP_PACA = 1 << 30 + * HWCAP_PACG = 1 << 31 + * HWCAP2_BTI = 1 << 17 + */ + private static final long AT_NULL = 0L; + private static final long AT_HWCAP = 16L; + private static final long AT_HWCAP2 = 26L; + + private static final long HWCAP_PACA = 1L << 30; + private static final long HWCAP_PACG = 1L << 31; + private static final long HWCAP2_BTI = 1L << 17; + + public static void main(String[] args) throws Exception { + String osName = System.getProperty("os.name", "unknown"); + String osArch = System.getProperty("os.arch", "unknown"); + String vmName = System.getProperty("java.vm.name", "unknown"); + String vmVersion = System.getProperty("java.vm.version", "unknown"); + String runtimeVersion = System.getProperty("java.runtime.version", "unknown"); + + System.out.println("=== JVM AArch64 PAC/BTI Probe ==="); + System.out.println("Process PID : " + currentPid()); + System.out.println("Java VM : " + vmName); + System.out.println("Java VM version : " + vmVersion); + System.out.println("Java runtime version : " + runtimeVersion); + System.out.println("os.name : " + osName); + System.out.println("os.arch : " + osArch); + System.out.println(); + + boolean linux = osName.toLowerCase(Locale.ROOT).contains("linux"); + boolean aarch64 = + osArch.equalsIgnoreCase("aarch64") || + osArch.equalsIgnoreCase("arm64"); + + if (!linux) { + System.out.println("FINAL RESULT: INCONCLUSIVE"); + System.out.println("Reason : This probe is Linux-specific because it uses /proc/self/auxv."); + System.exit(2); + } + + if (!aarch64) { + System.out.println("FINAL RESULT: INCONCLUSIVE"); + System.out.println("Reason : JVM is not executing as AArch64/arm64."); + System.exit(2); + } + + Map auxv; + try { + auxv = readAuxv(); + } catch (IOException ex) { + System.out.println("FINAL RESULT: INCONCLUSIVE"); + System.out.println("Reason : Could not read /proc/self/auxv from inside the JVM."); + System.out.println("Exception : " + ex); + System.exit(2); + return; + } + + long hwcap = auxv.getOrDefault(AT_HWCAP, 0L); + long hwcap2 = auxv.getOrDefault(AT_HWCAP2, 0L); + + boolean paca = (hwcap & HWCAP_PACA) != 0; + boolean pacg = (hwcap & HWCAP_PACG) != 0; + boolean pac = paca || pacg; + boolean bti = (hwcap2 & HWCAP2_BTI) != 0; + + System.out.printf("AT_HWCAP : 0x%016x%n", hwcap); + System.out.printf("AT_HWCAP2 : 0x%016x%n", hwcap2); + System.out.println(); + + System.out.println("Decoded AArch64 features visible to this JVM process:"); + System.out.println(" HWCAP_PACA : " + yesNo(paca)); + System.out.println(" HWCAP_PACG : " + yesNo(pacg)); + System.out.println(" PAC present : " + yesNo(pac)); + System.out.println(" HWCAP2_BTI : " + yesNo(bti)); + System.out.println(); + + if (pac && bti) { + System.out.println("FINAL RESULT: POSITIVE"); + System.out.println("Meaning : The JVM is executing on Linux/AArch64 with PAC and BTI exposed to userspace."); + System.exit(0); + } + + if (!pac && !bti) { + System.out.println("FINAL RESULT: NEGATIVE"); + System.out.println("Meaning : The JVM is executing on Linux/AArch64 without PAC and BTI exposed to userspace."); + System.out.println("Expected : This is the expected solid negative on Armv8 systems that lack PAC/BTI."); + System.exit(1); + } + + System.out.println("FINAL RESULT: INCONCLUSIVE"); + System.out.println("Meaning : Only part of the expected PAC/BTI feature set is exposed."); + System.out.println("Detail : PAC=" + yesNo(pac) + ", BTI=" + yesNo(bti)); + System.exit(2); + } + + private static String yesNo(boolean value) { + return value ? "YES" : "NO"; + } + + private static long currentPid() { + try { + return ProcessHandle.current().pid(); + } catch (Throwable ignored) { + String name = ManagementFactory.getRuntimeMXBean().getName(); + int at = name.indexOf('@'); + if (at > 0) { + try { + return Long.parseLong(name.substring(0, at)); + } catch (NumberFormatException ignoredAgain) { + return -1L; + } + } + return -1L; + } + } + + private static Map readAuxv() throws IOException { + byte[] data = readAll("/proc/self/auxv"); + + /* + * On AArch64 Linux, auxv entries in a 64-bit process are pairs of + * unsigned long values: + * + * a_type, a_val + * + * Java long is signed, but bit testing is unaffected. + */ + if (data.length % 16 != 0) { + throw new IOException("Unexpected auxv size for 64-bit process: " + data.length); + } + + ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.nativeOrder()); + Map out = new TreeMap<>(); + + while (bb.remaining() >= 16) { + long type = bb.getLong(); + long value = bb.getLong(); + + if (type == AT_NULL) { + break; + } + + out.put(type, value); + } + + return out; + } + + private static byte[] readAll(String path) throws IOException { + try (FileInputStream in = new FileInputStream(path); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + byte[] buf = new byte[4096]; + int n; + while ((n = in.read(buf)) >= 0) { + out.write(buf, 0, n); + } + return out.toByteArray(); + } + } +} +JAVA + +echo "=== OpenJDK JVM PAC/BTI platform exercise ===" +echo "Using java: ${JAVA_BIN}" +echo + +if ! command -v "${JAVA_BIN}" >/dev/null 2>&1; then + echo "FINAL RESULT: INCONCLUSIVE" + echo "Reason : java executable not found: ${JAVA_BIN}" + exit 2 +fi + +echo "--- java -version ---" +"${JAVA_BIN}" -version 2>&1 +echo + +echo "--- JVM branch-protection flag visibility check ---" +echo "This is informational. The final result comes from the JVM process auxv probe." +if "${JAVA_BIN}" -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version 2>&1 \ + | grep -E 'UseBranchProtection|BranchProtection' ; then + : +else + echo "No UseBranchProtection flag printed. This can happen with older JVMs or builds that do not expose the flag." +fi +echo + +echo "--- Optional runtime flag exercise: -XX:UseBranchProtection=standard ---" +echo "This should be accepted by branch-protection-aware OpenJDK builds; older builds may reject it." +if "${JAVA_BIN}" -XX:UseBranchProtection=standard -version >/tmp/jvm_pac_bti_flag_check.out 2>&1; then + echo "Accepted: -XX:UseBranchProtection=standard" +else + echo "Not accepted or not supported by this JVM:" + sed 's/^/ /' /tmp/jvm_pac_bti_flag_check.out +fi +rm -f /tmp/jvm_pac_bti_flag_check.out +echo + +echo "--- Running in-JVM auxv probe ---" + +# Prefer Java source-file mode, available in modern OpenJDK. +# Fall back to javac if needed. +"${JAVA_BIN}" "${tmpdir}/JvmAarch64PacBtiProbe.java" +rc=$? + +if [ "${rc}" -eq 0 ]; then + exit 0 +elif [ "${rc}" -eq 1 ]; then + exit 1 +else + if command -v javac >/dev/null 2>&1; then + echo + echo "Source-file mode failed or was unavailable; retrying with javac fallback..." + javac "${tmpdir}/JvmAarch64PacBtiProbe.java" && + "${JAVA_BIN}" -cp "${tmpdir}" JvmAarch64PacBtiProbe + exit $? + fi + + exit "${rc}" +fi +``` + +## Run the test script + +In your SSH session, run the test script to confirm PAC/BTI enablement: + +```bash +chmod 755 ./test-pacbti.sh +./test-pacbti.sh +``` + +Output should resemble: + +```output +=== OpenJDK JVM PAC/BTI platform exercise === +Using java: java + +--- java -version --- +openjdk version "17.0.13" 2024-10-15 +OpenJDK Runtime Environment (build 17.0.13+11-suse-150400.3.48.2-aarch64) +OpenJDK 64-Bit Server VM (build 17.0.13+11-suse-150400.3.48.2-aarch64, mixed mode, sharing) + +--- JVM branch-protection flag visibility check --- +This is informational. The final result comes from the JVM process auxv probe. +No UseBranchProtection flag printed. This can happen with older JVMs or builds that do not expose the flag. + +--- Optional runtime flag exercise: -XX:UseBranchProtection=standard --- +This should be accepted by branch-protection-aware OpenJDK builds; older builds may reject it. +Not accepted or not supported by this JVM: + Unrecognized VM option 'UseBranchProtection=standard' + Error: Could not create the Java Virtual Machine. + Error: A fatal exception has occurred. Program will exit. + +--- Running in-JVM auxv probe --- +=== JVM AArch64 PAC/BTI Probe === +Process PID : 3523 +Java VM : OpenJDK 64-Bit Server VM +Java VM version : 17.0.13+11-suse-150400.3.48.2-aarch64 +Java runtime version : 17.0.13+11-suse-150400.3.48.2-aarch64 +os.name : Linux +os.arch : aarch64 + +AT_HWCAP : 0x00000000efffffff +AT_HWCAP2 : 0x000000000003f3ff + +Decoded AArch64 features visible to this JVM process: + HWCAP_PACA : YES + HWCAP_PACG : YES + PAC present : YES + HWCAP2_BTI : YES + +FINAL RESULT: POSITIVE +Meaning : The JVM is executing on Linux/AArch64 with PAC and BTI exposed to userspace. +``` + +## What you've learned and what's next + +Most OpenJDK builds are distributed with PAC/BTI enabled but optionally used by default because they must remain compatible with older Arm platforms. When you need these protections, you can build and register your own JVM with branch protection support. + +When a PAC/BTI-enabled JVM runs on a platform that supports these features, Java workloads gain additional control-flow protection.