From dafde7ed629c64e66eeb4cc99fd6af9dfa932dab Mon Sep 17 00:00:00 2001 From: Jason Penilla <11360596+jpenilla@users.noreply.github.com> Date: Sun, 28 Apr 2024 12:43:06 -0700 Subject: [PATCH] Address todos --- ...-Modify-library-loader-jars-bytecode.patch | 131 +++++++++++++----- 1 file 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 35aba27d4..126560a15 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 modifier; ++ private final Map 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