aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJason Penilla <[email protected]>2024-04-28 12:43:06 -0700
committerJason Penilla <[email protected]>2024-04-28 12:43:06 -0700
commitdafde7ed629c64e66eeb4cc99fd6af9dfa932dab (patch)
treedd2cfbe27b9223edd3b42bd178c99ee8f10964b6
parentbe53945cc049f4884d826f582c578440523922ee (diff)
downloadPaper-library-reflection.tar.gz
Paper-library-reflection.zip
Address todoslibrary-reflection
-rw-r--r--patches/server/1046-Modify-library-loader-jars-bytecode.patch131
1 files changed, 100 insertions, 31 deletions
diff --git a/patches/server/1046-Modify-library-loader-jars-bytecode.patch b/patches/server/1046-Modify-library-loader-jars-bytecode.patch
index 35aba27d4c..126560a15e 100644
--- a/patches/server/1046-Modify-library-loader-jars-bytecode.patch
+++ b/patches/server/1046-Modify-library-loader-jars-bytecode.patch
@@ -6,31 +6,42 @@ Subject: [PATCH] Modify library loader jars bytecode
diff --git a/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java
new file mode 100644
-index 0000000000000000000000000000000000000000..acd24b8d32b5debd987f463c0be0ac6bdfdd061f
+index 0000000000000000000000000000000000000000..405416dc3d1c8c58b4e0c880d8751ca319188f62
--- /dev/null
+++ b/src/main/java/io/papermc/paper/plugin/entrypoint/classloader/BytecodeModifyingURLClassLoader.java
-@@ -0,0 +1,116 @@
+@@ -0,0 +1,185 @@
+package io.papermc.paper.plugin.entrypoint.classloader;
+
+import io.papermc.paper.pluginremap.reflect.ReflectionRemapper;
+import java.io.IOException;
+import java.io.InputStream;
++import java.io.UncheckedIOException;
++import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.CodeSigner;
+import java.security.CodeSource;
++import java.util.Map;
++import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Function;
++import java.util.jar.Attributes;
+import java.util.jar.Manifest;
++import org.checkerframework.checker.nullness.qual.Nullable;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
++import static java.util.Objects.requireNonNullElse;
++
+public final class BytecodeModifyingURLClassLoader extends URLClassLoader {
+ static {
+ ClassLoader.registerAsParallelCapable();
+ }
+
++ private static final Object MISSING_MANIFEST = new Object();
++
+ private final Function<byte[], byte[]> modifier;
++ private final Map<String, Object> manifests = new ConcurrentHashMap<>();
+
+ public BytecodeModifyingURLClassLoader(
+ final URL[] urls,
@@ -65,20 +76,8 @@ index 0000000000000000000000000000000000000000..acd24b8d32b5debd987f463c0be0ac6b
+ if (url != null) {
+ try {
+ result = this.defineClass(name, url);
-+ } catch (IOException e) {
++ } catch (final IOException e) {
+ throw new ClassNotFoundException(name, e);
-+ } catch (ClassFormatError e2) {
-+ /* TODO
-+ try {
-+ final Throwable dataError = (Throwable) GET_DATA_ERROR.invoke(res);
-+ if (dataError != null) {
-+ e2.addSuppressed(dataError);
-+ }
-+ } catch (final ReflectiveOperationException e) {
-+ throw new RuntimeException(e);
-+ }
-+ */
-+ throw e2;
+ }
+ } else {
+ result = null;
@@ -94,26 +93,24 @@ index 0000000000000000000000000000000000000000..acd24b8d32b5debd987f463c0be0ac6b
+ if (i != -1) {
+ String pkgname = name.substring(0, i);
+ // Check if package already loaded.
-+ Manifest man = null; // (Manifest) GET_MANIFEST.invoke(res); TODO
-+ //if (GET_AND_VERIFY_PKG.invoke(this, pkgname, man, url) == null) { // todo
-+ try {
-+ if (man != null) {
-+ definePackage(pkgname, man, url);
-+ } else {
-+ definePackage(pkgname, null, null, null, null, null, null, null);
-+ }
-+ } catch (IllegalArgumentException iae) {
-+ // parallel-capable class loaders: re-verify in case of a
-+ // race condition
-+ /* TODO
-+ if (GET_AND_VERIFY_PKG.invoke(this, pkgname, man, url) == null) {
++ final @Nullable Manifest man = this.manifestFor(url);
++ if (this.getAndVerifyPackage(pkgname, man, url) == null) {
++ try {
++ if (man != null) {
++ this.definePackage(pkgname, man, url);
++ } else {
++ this.definePackage(pkgname, null, null, null, null, null, null, null);
++ }
++ } catch (IllegalArgumentException iae) {
++ // parallel-capable class loaders: re-verify in case of a
++ // race condition
++ if (this.getAndVerifyPackage(pkgname, man, url) == null) {
+ // Should never happen
+ throw new AssertionError("Cannot find package " +
+ pkgname);
+ }
-+ */
++ }
+ }
-+ //} // todo
+ }
+ final byte[] bytes;
+ try (final InputStream is = url.openStream()) {
@@ -123,7 +120,79 @@ index 0000000000000000000000000000000000000000..acd24b8d32b5debd987f463c0be0ac6b
+ final byte[] modified = this.modifier.apply(bytes);
+
+ final CodeSource cs = new CodeSource(url, (CodeSigner[]) null);
-+ return defineClass(name, modified, 0, modified.length, cs);
++ return this.defineClass(name, modified, 0, modified.length, cs);
++ }
++
++ private Package getAndVerifyPackage(
++ String pkgname,
++ Manifest man, URL url
++ ) {
++ Package pkg = getDefinedPackage(pkgname);
++ if (pkg != null) {
++ // Package found, so check package sealing.
++ if (pkg.isSealed()) {
++ // Verify that code source URL is the same.
++ if (!pkg.isSealed(url)) {
++ throw new SecurityException(
++ "sealing violation: package " + pkgname + " is sealed");
++ }
++ } else {
++ // Make sure we are not attempting to seal the package
++ // at this code source URL.
++ if ((man != null) && this.isSealed(pkgname, man)) {
++ throw new SecurityException(
++ "sealing violation: can't seal package " + pkgname +
++ ": already loaded");
++ }
++ }
++ }
++ return pkg;
++ }
++
++ private boolean isSealed(String name, Manifest man) {
++ Attributes attr = man.getAttributes(name.replace('.', '/').concat("/"));
++ String sealed = null;
++ if (attr != null) {
++ sealed = attr.getValue(Attributes.Name.SEALED);
++ }
++ if (sealed == null) {
++ if ((attr = man.getMainAttributes()) != null) {
++ sealed = attr.getValue(Attributes.Name.SEALED);
++ }
++ }
++ return "true".equalsIgnoreCase(sealed);
++ }
++
++ private @Nullable Manifest manifestFor(final URL url) throws IOException {
++ Manifest man = null;
++ if (url.getProtocol().equals("jar")) {
++ try {
++ final Object computedManifest = this.manifests.computeIfAbsent(jarName(url), $ -> {
++ try {
++ final Manifest m = ((JarURLConnection) url.openConnection()).getManifest();
++ return requireNonNullElse(m, MISSING_MANIFEST);
++ } catch (final IOException e) {
++ throw new UncheckedIOException(e);
++ }
++ });
++ if (computedManifest instanceof Manifest found) {
++ man = found;
++ }
++ } catch (final UncheckedIOException e) {
++ throw e.getCause();
++ } catch (final IllegalArgumentException e) {
++ throw new IOException(e);
++ }
++ }
++ return man;
++ }
++
++ private static String jarName(final URL sourceUrl) {
++ final int exclamationIdx = sourceUrl.getPath().lastIndexOf('!');
++ if (exclamationIdx != -1) {
++ return sourceUrl.getPath().substring(0, exclamationIdx);
++ }
++ throw new IllegalArgumentException("Could not find jar for URL " + sourceUrl);
+ }
+}
diff --git a/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java b/src/main/java/io/papermc/paper/plugin/loader/PaperClasspathBuilder.java