From 9ef9475b61dd00ec61f6ed2c7577b23bb575f4fe Mon Sep 17 00:00:00 2001 From: "Kristian S. Stangeland" Date: Mon, 3 Feb 2014 01:38:52 +0100 Subject: [PATCH] Correct a concurrency bug discovered by mibby. FIXES #39. It's another lazy initialization problem. I only check a single field before initializing two related fields, which can cause problems when two threads execute handleLogin() concurrently. If thread A detects that PACKET_LOGIN_CLIENT is null, it updates both it and LOGIN_GAME_PROFILE. However, thread B may only see the PACKET_LOGIN_CLIENT update, and still believe LOGIN_GAME_PROFILE is NULL. Hence why it causes a NullPointerException in issue #39. --- .../injector/netty/ChannelInjector.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java index 0e6ab530..32062b61 100644 --- a/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java +++ b/ProtocolLib/src/main/java/com/comphenix/protocol/injector/netty/ChannelInjector.java @@ -320,15 +320,22 @@ class ChannelInjector extends ByteToMessageDecoder implements Injector { * @param packet - the packet. */ protected void handleLogin(Class packetClass, Object packet) { - // Initialize packet class - if (PACKET_LOGIN_CLIENT == null) { - PACKET_LOGIN_CLIENT = PacketType.Login.Client.START.getPacketClass(); - LOGIN_GAME_PROFILE = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, GameProfile.class, true); + Class loginClass = PACKET_LOGIN_CLIENT; + FieldAccessor loginClient = LOGIN_GAME_PROFILE; + + // Initialize packet class and login + if (loginClass == null) { + loginClass = PacketType.Login.Client.START.getPacketClass(); + PACKET_LOGIN_CLIENT = loginClass; + } + if (loginClient == null) { + loginClient = Accessors.getFieldAccessor(PACKET_LOGIN_CLIENT, GameProfile.class, true); + LOGIN_GAME_PROFILE = loginClient; } // See if we are dealing with the login packet - if (PACKET_LOGIN_CLIENT.equals(packetClass)) { - GameProfile profile = (GameProfile) LOGIN_GAME_PROFILE.get(packet); + if (loginClass.equals(packetClass)) { + GameProfile profile = (GameProfile) loginClient.get(packet); // Save the channel injector factory.cacheInjector(profile.getName(), this);