From f583b2db3e534bae4507bff9b5caa400e5329436 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Thu, 8 May 2025 10:59:23 +0200 Subject: [PATCH] stage2/ml9: Fix crash with SinytraConnector The "Sinytra Connector" mod uses a custom `SecureJar` implementation, which prior to this commit resulted in us doing an out of bounds memory read (returning what looks like a String, resulting in a class cast exception) because we were using the same unsafe getter that we were using for ML's `Jar` implementation. This commit fixes the issue by using a separate getter for each implementation class. This change alone is sufficient to fix the issue because we'll then get an explicit `NoSuchFieldException`, which we catch. However, to avoid unnessecary log spam, this commit additionally special cases the dummy class to silently return `null`. It also changes our `getVersion` method to still try the fallback paths (which do work!) instead of returning early on `null` since that is now a legitimate case. --- .../stage2/util/SortedJarOrPathList.java | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/stage2/modlauncher9/src/main/java/gg/essential/loader/stage2/util/SortedJarOrPathList.java b/stage2/modlauncher9/src/main/java/gg/essential/loader/stage2/util/SortedJarOrPathList.java index 93594ca..d26205a 100644 --- a/stage2/modlauncher9/src/main/java/gg/essential/loader/stage2/util/SortedJarOrPathList.java +++ b/stage2/modlauncher9/src/main/java/gg/essential/loader/stage2/util/SortedJarOrPathList.java @@ -14,6 +14,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; +import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; @@ -30,7 +31,7 @@ public class SortedJarOrPathList extends ArrayList { private static final Logger LOGGER = LogManager.getLogger(); private static final ArtifactVersion FALLBACK_VERSION = new DefaultArtifactVersion("1"); private static Function jarGetter; - private Function metadataGetter; + private final Map, Function> metadataGetters = new HashMap<>(); private BiFunction pathOrJarConstructor; private final Map versionCache = new IdentityHashMap<>(); @@ -53,9 +54,13 @@ public SortedJarOrPathList(CompatibilityLayer compatibilityLayer, Function`, resulting // in versions being reported as the string "Optional.empty" or "Optional[1.2.3]" instead of `null` or "1.2.3". @@ -119,13 +124,24 @@ public static SecureJar getJar(Object pathOrJar) { } private JarMetadata getMetadata(SecureJar jar) { + Class implClass = jar.getClass(); + Function metadataGetter = metadataGetters.get(implClass); if (metadataGetter == null) { try { - metadataGetter = UnsafeHacks.makeGetter(jar.getClass().getDeclaredField("metadata")); + String implName = implClass.getName(); + switch (implName) { + case "org.sinytra.connector.service.DummyVirtualJar": + // Used for the dummy fabric-loader mod as well as for code generated by ManninghamMills + metadataGetter = __ -> null; + break; + default: // probably the main ModLauncher-internal impl + metadataGetter = UnsafeHacks.makeGetter(implClass.getDeclaredField("metadata")); + } } catch (Throwable t) { - LOGGER.error("Failed to get metadata from " + jar.getClass() + ":", t); + LOGGER.error("Failed to get metadata from " + implClass + ":", t); metadataGetter = __ -> null; } + metadataGetters.put(implClass, metadataGetter); } return metadataGetter.apply(jar); }