ProtocolLib

3M Downloads

Race condition in EnumWrappers#initialize

diogotcorreia opened this issue ยท 0 comments

commented
  • This issue is not solved in a development build

Describe the bug
I'm using an async listener in my plugin, so it frequently happens that two packets that trigger initialization of EnumWrapper are sent at the same time.

However, due to the design of the EnumWrapper#initialize method, in an async context it is possible that the method returns without all fields having been initialized:

private static void initialize() {
    if (INITIALIZED)
        return; // (1)

    INITIALIZED = true; // (2)

    PROTOCOL_CLASS = MinecraftReflection.getEnumProtocolClass();
    // ...
    // (3)
}

If another thread calls initialize after the first thread reaches (2) but not (3), it will return immediately (1).

This error is a side effect of this issue (only happens once, when the first player joins the server):

[12:45:42 ERROR]: [Triton] Unhandled exception occurred in onAsyncPacket() for Triton
java.lang.NullPointerException: Cannot invoke "java.lang.Enum.name()" because "generic" is null
        at ProtocolLib.jar/com.comphenix.protocol.wrappers.EnumWrappers$EnumConverter.getSpecific(EnumWrappers.java:1104) ~[ProtocolLib.jar:?]
        at ProtocolLib.jar/com.comphenix.protocol.wrappers.EnumWrappers$EnumConverter.getSpecific(EnumWrappers.java:1090) ~[ProtocolLib.jar:?]
        at ProtocolLib.jar/com.comphenix.protocol.wrappers.WrappedTeamParameters.getColor(WrappedTeamParameters.java:73) ~[ProtocolLib.jar:?]
        at Triton.jar/com.rexcantor64.triton.packetinterceptor.ProtocolLibListener.handleScoreboardTeam(ProtocolLibListener.java:614) ~[Triton.jar:?]
        at Triton.jar/com.rexcantor64.triton.packetinterceptor.ProtocolLibListener.onPacketSending(ProtocolLibListener.java:755) ~[Triton.jar:?]
        at ProtocolLib.jar/com.comphenix.protocol.async.AsyncListenerHandler.lambda$processPacket$4(AsyncListenerHandler.java:602) ~[ProtocolLib.jar:?]
        at ProtocolLib.jar/com.comphenix.protocol.timing.TimingTracker.lambda$static$0(TimingTracker.java:7) ~[ProtocolLib.jar:?]
        at ProtocolLib.jar/com.comphenix.protocol.async.AsyncListenerHandler.processPacket(AsyncListenerHandler.java:600) ~[ProtocolLib.jar:?]
        at ProtocolLib.jar/com.comphenix.protocol.async.AsyncListenerHandler.listenerLoop(AsyncListenerHandler.java:572) ~[ProtocolLib.jar:?]
        at ProtocolLib.jar/com.comphenix.protocol.async.AsyncListenerHandler.access$100(AsyncListenerHandler.java:48) ~[ProtocolLib.jar:?]
        at ProtocolLib.jar/com.comphenix.protocol.async.AsyncListenerHandler$1.run(AsyncListenerHandler.java:217) ~[ProtocolLib.jar:?]
        at ProtocolLib.jar/com.comphenix.protocol.async.AsyncListenerHandler.lambda$start$1(AsyncListenerHandler.java:286) ~[ProtocolLib.jar:?]
        at org.bukkit.craftbukkit.scheduler.CraftTask.run(CraftTask.java:101) ~[paper-1.21.jar:1.21-68-8b35adc]
        at org.bukkit.craftbukkit.scheduler.CraftAsyncTask.run(CraftAsyncTask.java:57) ~[paper-1.21.jar:1.21-68-8b35adc]
        at com.destroystokyo.paper.ServerSchedulerReportingWrapper.run(ServerSchedulerReportingWrapper.java:22) ~[paper-1.21.jar:?]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
        at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]

To Reproduce

(I'm not providing detailed steps since I'm going to be submitting a PR in a sec)

  1. Setup Triton and TAB plugins in a server (e.g. Paper 1.21)
  2. Join the server
  3. Error on console (might only happen sometimes; it's a race condition after all)

Expected behavior
EnumWrappers#initialize should be thread-safe

Version Info
Built from commit 4aa344b

Additional context
Submitting a PR in a sec

Extra log where I added some print statements for extra clarity of bug:

[12:52:23 INFO]: [ProtocolLib] [STDOUT] INITIALIZED = true
[12:52:23 INFO]: [ProtocolLib] [STDOUT] CHAT_FORMATTING_CLASS = null
[12:52:23 INFO]: [ProtocolLib] [STDOUT] INITIALIZED = true
[12:52:23 INFO]: [ProtocolLib] [STDOUT] CHAT_FORMATTING_CLASS = null
[12:52:23 ERROR]: [Triton] Unhandled exception occurred in onAsyncPacket() for Triton
java.lang.NullPointerException: Cannot invoke "java.lang.Enum.name()" because "generic" is null
// -snip- same error as above
[12:52:23 INFO]: [Triton] [TRACE] Trying to get translation with key 'chat.test.0' in language 'en_GB'
[12:52:23 INFO]: [Triton] [TRACE] Found translation with key 'chat.test.0' in language 'en_GB'
[12:52:23 INFO]: [ProtocolLib] [STDOUT] INITIALIZED = true
[12:52:23 INFO]: [ProtocolLib] [STDOUT] CHAT_FORMATTING_CLASS = class net.minecraft.ChatFormatting
[12:52:23 INFO]: [ProtocolLib] [STDOUT] INITIALIZED = true
[12:52:23 INFO]: [ProtocolLib] [STDOUT] CHAT_FORMATTING_CLASS = class net.minecraft.ChatFormatting