-
Type: Bug
-
Resolution: Fixed
-
Priority: Minor
-
None
-
Affects Version/s: None
-
Environment:
This bug was reproduced on Mac OS X 10.14.6 and OpenJDK 17 (Zulu17.28+13-CA).
The bug was originally identified on another user's machine.
/version output which the bug was reproduced on:
[17:54:00] [Server thread/INFO]: This server is running CraftBukkit version 3246-Spigot-6c1c1b2-dc764e7 (MC: 1.17.1) (Implementing API version 1.17.1-R0.1-SNAPSHOT) [17:54:00] [Server thread/INFO]: Checking version, please wait... [17:54:00] [Thread-10/INFO]: You are running the latest version
This bug was reproduced on Mac OS X 10.14.6 and OpenJDK 17 (Zulu17.28+13-CA). The bug was originally identified on another user's machine. /version output which the bug was reproduced on: [17:54:00] [Server thread/INFO]: This server is running CraftBukkit version 3246-Spigot-6c1c1b2-dc764e7 (MC: 1.17.1) (Implementing API version 1.17.1-R0.1-SNAPSHOT) [17:54:00] [Server thread/INFO]: Checking version, please wait... [17:54:00] [ Thread -10/INFO]: You are running the latest version
-
CraftBukkit version 3246-Spigot-6c1c1b2-dc764e7 (MC: 1.17.1) (Implementing API version 1.17.1-R0.1-SNAPSHOT)
-
Yes
Expected Behavior
Plugins should not see each others' libraries from the "library loader" feature, except in the case of dependencies between plugins.
Actual Behavior
If and only if a class in a library has already been loaded, other plugins can see the loaded class even when they do not depend on the plugin providing the library. This is incorrect behavior.
How to use the Reproducer
Refer to https://github.com/A248/SpigotLibraryLoaderReproducer
- Build with maven, "mvn package"
- Add the two plugins created by the reproducer build to the /plugins directory
- Start the server
- Observe log output
Diagnosis
This bug occurs because PluginClassLoader performs a "super.loadClass" call.
Class<?> loadClass0(@NotNull String name, boolean resolve, boolean checkGlobal, boolean checkLibraries) throws ClassNotFoundException { try { return super.loadClass(name, resolve); } catch (ClassNotFoundException ex) { } // remaining implementation omitted
As specified in the documentation of ClassLoader, and which is confirmed by the implementation, ClassLoader#loadClass will find an existing loaded class before trying to find the class from a jar file. The logic of loadClass is as follows:
- If there is an existing Class instance defined to the ClassLoader, yield that Class.
- Otherwise, check the parent ClassLoader for the Class instance.
- Otherwise again, check the "findClass" method.
Per the reproducer, when PluginUsingLL loads the Caffeine class, the Caffeine class becomes defined to the ClassLoader of PluginUsingLL. When PluginIndependent later tries to load the Caffeine class, it can now see the Caffeine class from PluginUsingLL.