瀏覽代碼

Prepopulate the system's security manager setters map. (#80462)

Chris Hegarty 3 年之前
父節點
當前提交
4f00058d22

+ 3 - 1
server/src/main/java/org/elasticsearch/bootstrap/Elasticsearch.java

@@ -60,12 +60,14 @@ class Elasticsearch extends EnvironmentAwareCommand {
      */
     public static void main(final String[] args) throws Exception {
         overrideDnsCachePolicyProperties();
+        org.elasticsearch.bootstrap.Security.prepopulateSecurityCaller();
+
         /*
          * We want the JVM to think there is a security manager installed so that if internal policy decisions that would be based on the
          * presence of a security manager or lack thereof act as if there is a security manager present (e.g., DNS cache policy). This
          * forces such policies to take effect immediately.
          */
-        System.setSecurityManager(new SecurityManager() {
+        org.elasticsearch.bootstrap.Security.setSecurityManager(new SecurityManager() {
 
             @Override
             public void checkPermission(Permission perm) {

+ 54 - 1
server/src/main/java/org/elasticsearch/bootstrap/Security.java

@@ -8,6 +8,7 @@
 
 package org.elasticsearch.bootstrap;
 
+import org.elasticsearch.ElasticsearchException;
 import org.elasticsearch.cli.Command;
 import org.elasticsearch.common.settings.Settings;
 import org.elasticsearch.core.PathUtils;
@@ -20,6 +21,10 @@ import org.elasticsearch.secure_sm.SecureSM;
 import org.elasticsearch.transport.TcpTransport;
 
 import java.io.IOException;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.lang.reflect.Field;
 import java.net.SocketPermission;
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -38,6 +43,7 @@ import java.util.Map;
 import java.util.Set;
 import java.util.function.Consumer;
 
+import static java.lang.invoke.MethodType.methodType;
 import static org.elasticsearch.bootstrap.FilePermissionUtils.addDirectoryPath;
 import static org.elasticsearch.bootstrap.FilePermissionUtils.addSingleFilePath;
 
@@ -92,6 +98,10 @@ final class Security {
     /** no instantiation */
     private Security() {}
 
+    static void setSecurityManager(@SuppressWarnings("removal") SecurityManager sm) {
+        System.setSecurityManager(sm);
+    }
+
     /**
      * Initializes SecurityManager for the environment
      * Can only happen once!
@@ -117,7 +127,7 @@ final class Security {
             // SecureSM matches class names as regular expressions so we escape the $ that arises from the nested class name
             ElasticsearchUncaughtExceptionHandler.PrivilegedHaltAction.class.getName().replace("$", "\\$"),
             Command.class.getName() };
-        System.setSecurityManager(new SecureSM(classesThatCanExit));
+        setSecurityManager(new SecureSM(classesThatCanExit));
 
         // do some basic tests
         selfTest();
@@ -333,4 +343,47 @@ final class Security {
             throw new SecurityException("Security misconfiguration: cannot access java.io.tmpdir", problem);
         }
     }
+
+    /**
+     * Prepopulates the system's security manager callers map with this class as a caller.
+     * This is loathsome, but avoids the annoying warning message at run time.
+     * Returns true if the callers map has been populated.
+     */
+    static boolean prepopulateSecurityCaller() {
+        Field f;
+        try {
+            f = getDeclaredField(Class.forName("java.lang.System$CallersHolder", true, null), "callers");
+        } catch (NoSuchFieldException | ClassNotFoundException ignore) {
+            return false;
+        }
+        try {
+            Class<?> c = Class.forName("sun.misc.Unsafe");
+            MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(c, MethodHandles.lookup());
+            VarHandle handle = lookup.findStaticVarHandle(c, "theUnsafe", c);
+            Object theUnsafe = handle.get();
+            MethodHandle mh = lookup.findVirtual(c, "staticFieldBase", methodType(Object.class, Field.class));
+            mh = mh.asType(mh.type().changeParameterType(0, Object.class));
+            Object base = mh.invokeExact(theUnsafe, f);
+            mh = lookup.findVirtual(c, "staticFieldOffset", methodType(long.class, Field.class));
+            mh = mh.asType(mh.type().changeParameterType(0, Object.class));
+            long offset = (long) mh.invokeExact(theUnsafe, f);
+            mh = lookup.findVirtual(c, "getObject", methodType(Object.class, Object.class, long.class));
+            mh = mh.asType(mh.type().changeParameterType(0, Object.class));
+            Object callers = (Object) mh.invokeExact(theUnsafe, base, offset);
+            if (Map.class.isAssignableFrom(callers.getClass())) {
+                @SuppressWarnings("unchecked")
+                Map<Class<?>, Boolean> map = Map.class.cast(callers);
+                map.put(org.elasticsearch.bootstrap.Security.class, true);
+                return true;
+            }
+        } catch (Throwable t) {
+            throw new ElasticsearchException(t);
+        }
+        return false;
+    }
+
+    @SuppressForbidden(reason = "access violation required")
+    private static Field getDeclaredField(Class<?> c, String name) throws NoSuchFieldException {
+        return c.getDeclaredField(name);
+    }
 }

+ 27 - 0
server/src/test/java/org/elasticsearch/bootstrap/NoSecurityManagerTests.java

@@ -0,0 +1,27 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.bootstrap;
+
+import org.apache.lucene.util.LuceneTestCase;
+
+import static org.hamcrest.Matchers.is;
+
+public class NoSecurityManagerTests extends LuceneTestCase {
+
+    public void testPrepopulateSecurityCaller() {
+        assumeTrue("Unexpected security manager:" + System.getSecurityManager(), System.getSecurityManager() == null);
+        boolean isAtLeastJava17 = Runtime.version().feature() >= 17;
+        boolean isPrepopulated = Security.prepopulateSecurityCaller();
+        if (isAtLeastJava17) {
+            assertThat(isPrepopulated, is(true));
+        } else {
+            assertThat(isPrepopulated, is(false));
+        }
+    }
+}