From d9370b4f65c9901793f7e22ba310b38afa6ce43e Mon Sep 17 00:00:00 2001 From: IzzelAliz Date: Sat, 24 Apr 2021 13:27:32 +0800 Subject: [PATCH] Fix signed jar class caching (#241) --- .../mixin/bukkit/PluginClassLoaderMixin.java | 15 ++---- .../util/remapper/ClassLoaderRemapper.java | 50 +++++++++++++++++-- 2 files changed, 50 insertions(+), 15 deletions(-) diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/PluginClassLoaderMixin.java b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/PluginClassLoaderMixin.java index 8214caf5..57fafc9d 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/PluginClassLoaderMixin.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mixin/bukkit/PluginClassLoaderMixin.java @@ -6,6 +6,7 @@ import io.izzel.arclight.common.bridge.bukkit.JavaPluginLoaderBridge; import io.izzel.arclight.common.mod.util.remapper.ArclightRemapper; import io.izzel.arclight.common.mod.util.remapper.ClassLoaderRemapper; import io.izzel.arclight.common.mod.util.remapper.RemappingClassLoader; +import io.izzel.tools.product.Product2; import org.bukkit.Bukkit; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.java.JavaPluginLoader; @@ -16,11 +17,9 @@ import org.spongepowered.asm.mixin.Shadow; import java.io.IOException; import java.io.InputStream; -import java.net.JarURLConnection; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; -import java.security.CodeSigner; import java.security.CodeSource; import java.util.Map; import java.util.concurrent.Callable; @@ -74,16 +73,10 @@ public class PluginClassLoaderMixin extends URLClassLoader implements RemappingC if (url != null) { URLConnection connection; - CodeSigner[] signers; Callable byteSource; try { connection = url.openConnection(); connection.connect(); - if (connection instanceof JarURLConnection) { - signers = ((JarURLConnection) connection).getJarEntry().getCodeSigners(); - } else { - signers = new CodeSigner[0]; - } byteSource = () -> { try (InputStream is = connection.getInputStream()) { byte[] classBytes = ByteStreams.toByteArray(is); @@ -96,7 +89,7 @@ public class PluginClassLoaderMixin extends URLClassLoader implements RemappingC throw new ClassNotFoundException(name, e); } - byte[] classBytes = this.getRemapper().remapClass(name, byteSource, connection); + Product2 classBytes = this.getRemapper().remapClass(name, byteSource, connection); int dot = name.lastIndexOf('.'); if (dot != -1) { @@ -116,9 +109,7 @@ public class PluginClassLoaderMixin extends URLClassLoader implements RemappingC } } - CodeSource source = new CodeSource(this.url, signers); - - result = defineClass(name, classBytes, 0, classBytes.length, source); + result = defineClass(name, classBytes._1, 0, classBytes._1.length, classBytes._2); } if (result == null) { diff --git a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRemapper.java b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRemapper.java index c69373f8..67a53043 100644 --- a/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRemapper.java +++ b/arclight-common/src/main/java/io/izzel/arclight/common/mod/util/remapper/ClassLoaderRemapper.java @@ -4,9 +4,12 @@ import com.google.common.base.Preconditions; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Maps; +import com.google.common.io.ByteStreams; import io.izzel.arclight.api.Unsafe; import io.izzel.arclight.common.mod.util.remapper.generated.ArclightReflectionHandler; import io.izzel.arclight.i18n.ArclightConfig; +import io.izzel.tools.product.Product; +import io.izzel.tools.product.Product2; import net.md_5.specialsource.JarMapping; import net.md_5.specialsource.JarRemapper; import net.md_5.specialsource.RemappingClassAdapter; @@ -28,8 +31,12 @@ import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.net.JarURLConnection; +import java.net.URL; import java.net.URLConnection; import java.nio.file.Files; +import java.security.CodeSigner; +import java.security.CodeSource; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; @@ -39,6 +46,7 @@ import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; +import java.util.jar.JarFile; public class ClassLoaderRemapper extends LenientJarRemapper { @@ -52,6 +60,7 @@ public class ClassLoaderRemapper extends LenientJarRemapper { private final String generatedHandler; private final Class generatedHandlerClass; private final GeneratedHandlerAdapter generatedHandlerAdapter; + private final Map secureJarInfo = new ConcurrentHashMap<>(); public ClassLoaderRemapper(JarMapping jarMapping, JarMapping toBukkitMapping, ClassLoader classLoader) { super(jarMapping); @@ -279,7 +288,19 @@ public class ClassLoaderRemapper extends LenientJarRemapper { return Maps.immutableEntry(owner, mapped); } - public byte[] remapClass(String className, Callable byteSource, URLConnection connection) throws ClassNotFoundException { + private boolean isSecureJar(JarFile jarFile) { + return this.secureJarInfo.computeIfAbsent(jarFile.getName(), key -> + jarFile.stream().anyMatch(it -> { + if (it.isDirectory()) return false; + String name = it.getName(); + return name.startsWith("META-INF") && (name.endsWith(".DSA") || + name.endsWith(".RSA") || + name.endsWith(".EC") || + name.endsWith(".SF")); + })); + } + + public Product2 remapClass(String className, Callable byteSource, URLConnection connection) throws ClassNotFoundException { try { ArclightClassCache.CacheSegment segment = ArclightClassCache.instance().makeSegment(connection); Optional optional = segment.findByName(className); @@ -287,7 +308,21 @@ public class ClassLoaderRemapper extends LenientJarRemapper { byte[] bytes = optional.get(); ClassWriter cw = new ClassWriter(0); new ClassReader(bytes).accept(new ClassRemapper(cw, generatedHandlerAdapter), 0); - return cw.toByteArray(); + URL url; + CodeSigner[] signers; + if (connection instanceof JarURLConnection) { + url = ((JarURLConnection) connection).getJarFileURL(); + if (isSecureJar(((JarURLConnection) connection).getJarFile())) { + ByteStreams.exhaust(connection.getInputStream()); // must read before asking signers + signers = ((JarURLConnection) connection).getJarEntry().getCodeSigners(); + } else { + signers = null; + } + } else { + url = connection.getURL(); + signers = null; + } + return Product.of(cw.toByteArray(), new CodeSource(url, signers)); } else { byte[] bytes = remapClassFile(byteSource.call(), GlobalClassRepo.INSTANCE); if (ArclightConfig.spec().getOptimization().isCachePluginClass()) { @@ -296,7 +331,16 @@ public class ClassLoaderRemapper extends LenientJarRemapper { byte[] store = cw.toByteArray(); segment.addToCache(className, store); } - return bytes; + URL url; + CodeSigner[] signers; + if (connection instanceof JarURLConnection) { + url = ((JarURLConnection) connection).getJarFileURL(); + signers = ((JarURLConnection) connection).getJarEntry().getCodeSigners(); + } else { + url = connection.getURL(); + signers = null; + } + return Product.of(bytes, new CodeSource(url, signers)); } } catch (Exception e) { throw new ClassNotFoundException(className, e);