Skip to content

Commit

Permalink
Allow SDK to run in environments prohibiting use of sun.misc.Unsafe (o…
Browse files Browse the repository at this point in the history
…pen-telemetry#4902)

Some applications run under strict java.security permissions
which do not allow access to sun.misc.Unsafe.

BatchSpanProcessor uses Unsafe via jctools, but has a fallback to
ArrayBlockingQueue. Extending that fallback rule to cover
java security exceptions as well.

Co-authored-by: Jack Berg <jberg@newrelic.com>
  • Loading branch information
2 people authored and dmarkwat committed Dec 30, 2022
1 parent 96c6c76 commit 52d2fea
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 2 deletions.
5 changes: 5 additions & 0 deletions sdk/trace-shaded-deps/build.gradle.kts
Expand Up @@ -26,3 +26,8 @@ tasks {
into("build/extracted/shadow")
}
}

tasks.withType<Test>().configureEach {
// JcToolsSecurityManagerTest interferes with JcToolsTest
setForkEvery(1)
}
Expand Up @@ -5,9 +5,13 @@

package io.opentelemetry.sdk.trace.internal;

import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jctools.queues.MessagePassingQueue;
import org.jctools.queues.MpscArrayQueue;

Expand All @@ -19,14 +23,24 @@
*/
public final class JcTools {

private static final AtomicBoolean queueCreationWarningLogged = new AtomicBoolean();
private static final Logger logger = Logger.getLogger(JcTools.class.getName());

/**
* Returns a new {@link Queue} appropriate for use with multiple producers and a single consumer.
*/
public static <T> Queue<T> newFixedSizeQueue(int capacity) {
try {
return new MpscArrayQueue<>(capacity);
} catch (java.lang.NoClassDefFoundError e) {
// Happens when modules such as jdk.unsupported are disabled in a custom JRE distribution
} catch (java.lang.NoClassDefFoundError | java.lang.ExceptionInInitializerError e) {
if (!queueCreationWarningLogged.getAndSet(true)) {
logger.log(
Level.WARNING,
"Cannot create high-performance queue, reverting to ArrayBlockingQueue ({0})",
Objects.toString(e, "unknown cause"));
}
// Happens when modules such as jdk.unsupported are disabled in a custom JRE distribution,
// or a security manager preventing access to Unsafe is installed.
return new ArrayBlockingQueue<>(capacity);
}
}
Expand Down
@@ -0,0 +1,38 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.trace.internal;

import static org.assertj.core.api.Assertions.assertThat;

import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnJre;
import org.junit.jupiter.api.condition.JRE;

public class JcToolsSecurityManagerTest {

@Test
@EnabledOnJre({JRE.JAVA_8, JRE.JAVA_11, JRE.JAVA_17})
@SuppressLogger(JcTools.class)
void newFixedSizeQueue_SunMiscProhibited() {
assertThat(System.getSecurityManager()).isNull();
SunMiscProhibitedSecurityManager testingSecurityManager =
new SunMiscProhibitedSecurityManager();
try {
System.setSecurityManager(testingSecurityManager);
Queue<Object> queue =
AccessController.doPrivileged(
(PrivilegedAction<Queue<Object>>) () -> JcTools.newFixedSizeQueue(10));
assertThat(queue).isInstanceOf(ArrayBlockingQueue.class);
} finally {
System.setSecurityManager(null);
}
}
}
@@ -0,0 +1,111 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.trace.internal;

import java.io.FileDescriptor;
import java.net.InetAddress;
import java.security.AccessControlException;
import java.security.Permission;

/**
* A security manager which disallows access to classes in sun.misc. Running the tests with a
* standard security manager is too invasive.
*/
public class SunMiscProhibitedSecurityManager extends SecurityManager {

public SunMiscProhibitedSecurityManager() {}

@Override
protected Class<?>[] getClassContext() {
return super.getClassContext();
}

@Override
public void checkPermission(Permission perm) {
if (perm.getName().equals("accessClassInPackage.sun.misc")) {
throw new AccessControlException("access denied " + perm, perm);
}
}

@Override
public void checkPermission(Permission perm, Object context) {}

@Override
public void checkCreateClassLoader() {}

@Override
public void checkAccess(Thread t) {}

@Override
public void checkAccess(ThreadGroup g) {}

@Override
public void checkExit(int status) {}

@Override
public void checkExec(String cmd) {}

@Override
public void checkLink(String lib) {}

@Override
public void checkRead(FileDescriptor fd) {}

@Override
public void checkRead(String file) {}

@Override
public void checkRead(String file, Object context) {}

@Override
public void checkWrite(FileDescriptor fd) {}

@Override
public void checkWrite(String file) {}

@Override
public void checkDelete(String file) {}

@Override
public void checkConnect(String host, int port) {}

@Override
public void checkConnect(String host, int port, Object context) {}

@Override
public void checkListen(int port) {}

@Override
public void checkAccept(String host, int port) {}

@Override
public void checkMulticast(InetAddress maddr) {}

@Override
public void checkPropertiesAccess() {}

@Override
public void checkPropertyAccess(String key) {}

@Override
public void checkPrintJobAccess() {}

@Override
public void checkPackageAccess(String pkg) {
if (pkg.equals("sun.misc")) {
super.checkPackageAccess(pkg);
}
}

@Override
public void checkPackageDefinition(String pkg) {}

@Override
public void checkSetFactory() {}

@Override
public void checkSecurityAccess(String target) {}
}
@@ -0,0 +1,41 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.trace.internal;

import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

import java.security.AccessControlException;
import org.junit.jupiter.api.Test;

class SunMiscProhibitedSecurityManagerTest {

@Test
public void checkPackageAccess_ProhibitsSunMisc() {
SunMiscProhibitedSecurityManager sm = new SunMiscProhibitedSecurityManager();
assertThatThrownBy(() -> sm.checkPackageAccess("sun.misc"))
.isInstanceOf(AccessControlException.class)
.hasMessage(
"access denied (\"java.lang.RuntimePermission\" \"accessClassInPackage.sun.misc\")");
}

@Test
public void checkPackageAccess_ProhibitsSunMiscRuntimePermission() {
SunMiscProhibitedSecurityManager sm = new SunMiscProhibitedSecurityManager();

assertThatThrownBy(
() -> sm.checkPermission(new RuntimePermission("accessClassInPackage.sun.misc")))
.isInstanceOf(AccessControlException.class)
.hasMessage(
"access denied (\"java.lang.RuntimePermission\" \"accessClassInPackage.sun.misc\")");
}

@Test
public void checkPackageAccess_AllowsOtherPackage() {
SunMiscProhibitedSecurityManager sm = new SunMiscProhibitedSecurityManager();
assertThatNoException().isThrownBy(() -> sm.checkPackageAccess("io.opentelemetry.sdk"));
}
}

0 comments on commit 52d2fea

Please sign in to comment.