From ed7cc24b36766d031f45ad6573e9d8d3bfffcbc4 Mon Sep 17 00:00:00 2001 From: Andrew Ross Date: Thu, 23 Apr 2026 00:50:08 +0000 Subject: [PATCH] Remove Unsafe class injection from Java agent The Java agent previously used ClassInjector.UsingUnsafe at premain time to inject AgentPolicy, SubjectInterceptor, and the two stack caller chain extractors into the boot classloader. This relied on sun.misc.Unsafe, which is deprecated for removal on recent JDKs and emits JVM warnings. This change relocates StackCallerClassChainExtractor, StackCallerProtectionDomainChainExtractor, and SubjectInterceptor from :libs:agent-sm:agent into :libs:agent-sm:bootstrap, under a new org.opensearch.javaagent.bootstrap.internal subpackage to distinguish them from AgentPolicy, which is the shared API between the server and the agent. The bootstrap jar is listed on the agent jar's Boot-Class-Path manifest attribute, so the JVM loads these classes into the boot classloader natively with no runtime injection needed. Also set -Dnet.bytebuddy.safe=true on test JVMs to disable the Unsafe-based dispatchers inside ByteBuddy's ClassInjector. ByteBuddy falls back to MethodHandles.Lookup / reflection injection, which works on JDK 9+. Signed-off-by: Andrew Ross --- build.gradle | 6 +++++- .../secure_sm/AccessController.java | 2 +- .../java/org/opensearch/javaagent/Agent.java | 19 +------------------ .../opensearch/javaagent/FileInterceptor.java | 2 ++ .../javaagent/RuntimeHaltInterceptor.java | 1 + .../javaagent/SocketChannelInterceptor.java | 1 + .../javaagent/SystemExitInterceptor.java | 1 + ...kCallerProtectionDomainExtractorTests.java | 1 + .../StackCallerClassChainExtractor.java | 2 +- ...kCallerProtectionDomainChainExtractor.java | 2 +- .../internal}/SubjectInterceptor.java | 2 +- .../bootstrap/internal/package-info.java | 16 ++++++++++++++++ 12 files changed, 32 insertions(+), 23 deletions(-) rename libs/agent-sm/{agent/src/main/java/org/opensearch/javaagent => bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal}/StackCallerClassChainExtractor.java (95%) rename libs/agent-sm/{agent/src/main/java/org/opensearch/javaagent => bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal}/StackCallerProtectionDomainChainExtractor.java (97%) rename libs/agent-sm/{agent/src/main/java/org/opensearch/javaagent => bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal}/SubjectInterceptor.java (92%) create mode 100644 libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/package-info.java diff --git a/build.gradle b/build.gradle index 5aef923c77ac7..550b94f60900e 100644 --- a/build.gradle +++ b/build.gradle @@ -421,7 +421,11 @@ gradle.projectsEvaluated { task.jvmArgs += [ "--add-modules=jdk.incubator.vector", "--add-exports=java.base/com.sun.crypto.provider=ALL-UNNAMED", - "--enable-native-access=ALL-UNNAMED" + "--enable-native-access=ALL-UNNAMED", + // Disable ByteBuddy's Unsafe-based class injection path to avoid + // "sun.misc.Unsafe::objectFieldOffset has been called by ByteBuddy" JVM warnings on JDK 21+. + // ByteBuddy still falls back to Lookup/Reflection injection strategies. + "-Dnet.bytebuddy.safe=true" ] // Add Java Agent for security sandboxing diff --git a/libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/AccessController.java b/libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/AccessController.java index b07bb9068e8fa..e7c27ead15ff1 100644 --- a/libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/AccessController.java +++ b/libs/agent-sm/agent-policy/src/main/java/org/opensearch/secure_sm/AccessController.java @@ -17,7 +17,7 @@ * removal. All new code should use this class instead of the JDK's {@code AccessController}. * * Running code in a privileged context will ensure that the code has the necessary permissions - * without traversing through the entire call stack. See {@code org.opensearch.javaagent.StackCallerProtectionDomainChainExtractor} + * without traversing through the entire call stack. See {@code org.opensearch.javaagent.bootstrap.internal.StackCallerProtectionDomainChainExtractor} * * Example usages: *
diff --git a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/Agent.java b/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/Agent.java
index f638d354fdd7b..6f3098eae655f 100644
--- a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/Agent.java
+++ b/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/Agent.java
@@ -8,7 +8,7 @@
 
 package org.opensearch.javaagent;
 
-import org.opensearch.javaagent.bootstrap.AgentPolicy;
+import org.opensearch.javaagent.bootstrap.internal.SubjectInterceptor;
 
 import javax.security.auth.Subject;
 
@@ -18,14 +18,11 @@
 import java.nio.channels.SocketChannel;
 import java.nio.file.Files;
 import java.nio.file.spi.FileSystemProvider;
-import java.util.Map;
 
 import net.bytebuddy.ByteBuddy;
 import net.bytebuddy.agent.builder.AgentBuilder;
 import net.bytebuddy.asm.Advice;
 import net.bytebuddy.description.type.TypeDescription;
-import net.bytebuddy.dynamic.ClassFileLocator;
-import net.bytebuddy.dynamic.loading.ClassInjector;
 import net.bytebuddy.implementation.Implementation;
 import net.bytebuddy.implementation.MethodDelegation;
 import net.bytebuddy.matcher.ElementMatcher.Junction;
@@ -96,20 +93,6 @@ private static AgentBuilder createAgentBuilder() throws Exception {
             ElementMatchers.named("getSubject")
         ).intercept(MethodDelegation.to(SubjectInterceptor.class));
 
-        ClassInjector.UsingUnsafe.ofBootLoader()
-            .inject(
-                Map.of(
-                    new TypeDescription.ForLoadedType(StackCallerProtectionDomainChainExtractor.class),
-                    ClassFileLocator.ForClassLoader.read(StackCallerProtectionDomainChainExtractor.class),
-                    new TypeDescription.ForLoadedType(StackCallerClassChainExtractor.class),
-                    ClassFileLocator.ForClassLoader.read(StackCallerClassChainExtractor.class),
-                    new TypeDescription.ForLoadedType(AgentPolicy.class),
-                    ClassFileLocator.ForClassLoader.read(AgentPolicy.class),
-                    new TypeDescription.ForLoadedType(SubjectInterceptor.class),
-                    ClassFileLocator.ForClassLoader.read(SubjectInterceptor.class)
-                )
-            );
-
         final ByteBuddy byteBuddy = new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE);
         var builder = new AgentBuilder.Default(byteBuddy).with(AgentBuilder.InitializationStrategy.NoOp.INSTANCE)
             .with(AgentBuilder.RedefinitionStrategy.REDEFINITION)
diff --git a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/FileInterceptor.java b/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/FileInterceptor.java
index 455be2a83f840..68dcfe0015d74 100644
--- a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/FileInterceptor.java
+++ b/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/FileInterceptor.java
@@ -9,6 +9,8 @@
 package org.opensearch.javaagent;
 
 import org.opensearch.javaagent.bootstrap.AgentPolicy;
+import org.opensearch.javaagent.bootstrap.internal.StackCallerClassChainExtractor;
+import org.opensearch.javaagent.bootstrap.internal.StackCallerProtectionDomainChainExtractor;
 
 import java.io.FilePermission;
 import java.lang.reflect.Method;
diff --git a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/RuntimeHaltInterceptor.java b/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/RuntimeHaltInterceptor.java
index 9f879a744f45f..d9edfdaa7223d 100644
--- a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/RuntimeHaltInterceptor.java
+++ b/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/RuntimeHaltInterceptor.java
@@ -9,6 +9,7 @@
 package org.opensearch.javaagent;
 
 import org.opensearch.javaagent.bootstrap.AgentPolicy;
+import org.opensearch.javaagent.bootstrap.internal.StackCallerClassChainExtractor;
 
 import java.lang.StackWalker.Option;
 import java.security.Policy;
diff --git a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SocketChannelInterceptor.java b/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SocketChannelInterceptor.java
index 93daeccb6503f..d98804092aece 100644
--- a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SocketChannelInterceptor.java
+++ b/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SocketChannelInterceptor.java
@@ -9,6 +9,7 @@
 package org.opensearch.javaagent;
 
 import org.opensearch.javaagent.bootstrap.AgentPolicy;
+import org.opensearch.javaagent.bootstrap.internal.StackCallerProtectionDomainChainExtractor;
 
 import java.lang.reflect.Method;
 import java.net.InetSocketAddress;
diff --git a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SystemExitInterceptor.java b/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SystemExitInterceptor.java
index 6ba4f59e00942..b19e5559cd5e3 100644
--- a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SystemExitInterceptor.java
+++ b/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SystemExitInterceptor.java
@@ -9,6 +9,7 @@
 package org.opensearch.javaagent;
 
 import org.opensearch.javaagent.bootstrap.AgentPolicy;
+import org.opensearch.javaagent.bootstrap.internal.StackCallerClassChainExtractor;
 
 import java.lang.StackWalker.Option;
 import java.security.Policy;
diff --git a/libs/agent-sm/agent/src/test/java/org/opensearch/javaagent/StackCallerProtectionDomainExtractorTests.java b/libs/agent-sm/agent/src/test/java/org/opensearch/javaagent/StackCallerProtectionDomainExtractorTests.java
index 2efb993448dc3..0ef5939fb8ed1 100644
--- a/libs/agent-sm/agent/src/test/java/org/opensearch/javaagent/StackCallerProtectionDomainExtractorTests.java
+++ b/libs/agent-sm/agent/src/test/java/org/opensearch/javaagent/StackCallerProtectionDomainExtractorTests.java
@@ -8,6 +8,7 @@
 
 package org.opensearch.javaagent;
 
+import org.opensearch.javaagent.bootstrap.internal.StackCallerProtectionDomainChainExtractor;
 import org.junit.Assume;
 import org.junit.Test;
 
diff --git a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/StackCallerClassChainExtractor.java b/libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/StackCallerClassChainExtractor.java
similarity index 95%
rename from libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/StackCallerClassChainExtractor.java
rename to libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/StackCallerClassChainExtractor.java
index b7be2883b6a79..4cf4b9a9a567f 100644
--- a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/StackCallerClassChainExtractor.java
+++ b/libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/StackCallerClassChainExtractor.java
@@ -6,7 +6,7 @@
  * compatible open source license.
  */
 
-package org.opensearch.javaagent;
+package org.opensearch.javaagent.bootstrap.internal;
 
 import java.lang.StackWalker.StackFrame;
 import java.util.Collection;
diff --git a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/StackCallerProtectionDomainChainExtractor.java b/libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/StackCallerProtectionDomainChainExtractor.java
similarity index 97%
rename from libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/StackCallerProtectionDomainChainExtractor.java
rename to libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/StackCallerProtectionDomainChainExtractor.java
index da2c00cd8a3f3..607678c1bb796 100644
--- a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/StackCallerProtectionDomainChainExtractor.java
+++ b/libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/StackCallerProtectionDomainChainExtractor.java
@@ -6,7 +6,7 @@
  * compatible open source license.
  */
 
-package org.opensearch.javaagent;
+package org.opensearch.javaagent.bootstrap.internal;
 
 import java.lang.StackWalker.StackFrame;
 import java.security.ProtectionDomain;
diff --git a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SubjectInterceptor.java b/libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/SubjectInterceptor.java
similarity index 92%
rename from libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SubjectInterceptor.java
rename to libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/SubjectInterceptor.java
index 1950a2ffce906..d684c8859f9b6 100644
--- a/libs/agent-sm/agent/src/main/java/org/opensearch/javaagent/SubjectInterceptor.java
+++ b/libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/SubjectInterceptor.java
@@ -6,7 +6,7 @@
  * compatible open source license.
  */
 
-package org.opensearch.javaagent;
+package org.opensearch.javaagent.bootstrap.internal;
 
 import javax.security.auth.Subject;
 
diff --git a/libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/package-info.java b/libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/package-info.java
new file mode 100644
index 0000000000000..13a7d2a6a1e4d
--- /dev/null
+++ b/libs/agent-sm/bootstrap/src/main/java/org/opensearch/javaagent/bootstrap/internal/package-info.java
@@ -0,0 +1,16 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * The OpenSearch Contributors require contributions made to
+ * this file be licensed under the Apache-2.0 license or a
+ * compatible open source license.
+ */
+
+/**
+ * Internal agent support classes that must be loaded by the boot classloader
+ * so that bytecode woven into JDK classes (either inlined ByteBuddy Advice or
+ * MethodDelegation stubs) can resolve them. These classes are implementation
+ * details of the Java agent and are not part of any public API; do not depend
+ * on them from outside {@code :libs:agent-sm:agent}.
+ */
+package org.opensearch.javaagent.bootstrap.internal;