From 3e3a748eb6ac80c52792519a99b6bfef7522f22d Mon Sep 17 00:00:00 2001 From: James Baldassari Date: Mon, 9 Sep 2019 15:08:21 -0400 Subject: [PATCH] SocksAuthRequest constructor occasionally throws IllegalStateException (#9558) Motivation: There appears to be a thread-safety issue in the way that `SocksAuthRequest` is using its `CharsetEncoder` instance. `CharsetUtil#encoder` returns a cached thread-local encoder instance, so it is not correct to store this instance in a static member variable and reuse it across multiple threads. The result is an occasional `IllegalStateException` as in the following example: ``` java.lang.IllegalStateException: Current state = RESET, new state = FLUSHED at java.base/java.nio.charset.CharsetEncoder.throwIllegalStateException(CharsetEncoder.java:989) at java.base/java.nio.charset.CharsetEncoder.flush(CharsetEncoder.java:672) at java.base/java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:801) at java.base/java.nio.charset.CharsetEncoder.canEncode(CharsetEncoder.java:907) at java.base/java.nio.charset.CharsetEncoder.canEncode(CharsetEncoder.java:982) at io.netty.handler.codec.socks.SocksAuthRequest.(SocksAuthRequest.java:43) ``` Modification: Instead of retrieving the thread-local encoder instance once and storing it as a static member instance, the encoder should be retrieved each time the constructor is invoked. This change prevents any potential concurrency issues where multiple threads may end up using the same encoder instance. Result: Fixes #9556. --- .../java/io/netty/handler/codec/socks/SocksAuthRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequest.java index 277e0d339b..78b6678caf 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequest.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequest.java @@ -27,7 +27,6 @@ import java.nio.charset.CharsetEncoder; * @see SocksAuthRequestDecoder */ public final class SocksAuthRequest extends SocksRequest { - private static final CharsetEncoder asciiEncoder = CharsetUtil.encoder(CharsetUtil.US_ASCII); private static final SocksSubnegotiationVersion SUBNEGOTIATION_VERSION = SocksSubnegotiationVersion.AUTH_PASSWORD; private final String username; private final String password; @@ -40,6 +39,7 @@ public final class SocksAuthRequest extends SocksRequest { if (password == null) { throw new NullPointerException("username"); } + final CharsetEncoder asciiEncoder = CharsetUtil.encoder(CharsetUtil.US_ASCII); if (!asciiEncoder.canEncode(username) || !asciiEncoder.canEncode(password)) { throw new IllegalArgumentException( "username: " + username + " or password: **** values should be in pure ascii");