mojira.dev
MC-267686

Incorrect packet writing in ClientboundLoginDisconnectPacket

This bug was introduced in 23w42a

Previously, the maximum string length of serialized components was 262144 characters, but starting from 23w42a, it has been changed to 32767. (Only writing. reading is still 262144)

How to Reproduce:
1. Set the ban reason for the player to be over 32704 characters.
2. Connect to the server.

Stacktrace:

[15:11:15] [Netty Server IO #2/ERROR]: Error receiving packet 0
io.netty.handler.codec.EncoderException: String too big (was 32768 characters, max 32767)
        at uv.a(SourceFile:38) ~[server-1.20.4.jar:?]
        at ui.a(SourceFile:637) ~[server-1.20.4.jar:?]
        at ui.a(SourceFile:633) ~[server-1.20.4.jar:?]
        at afr.a(SourceFile:23) ~[server-1.20.4.jar:?]
        at um.a(SourceFile:47) ~[server-1.20.4.jar:?]
        at um.encode(SourceFile:15) ~[server-1.20.4.jar:?]
        at io.netty.handler.codec.MessageToByteEncoder.write(MessageToByteEncoder.java:107) ~[netty-codec-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:881) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:863) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:968) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:856) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.handler.codec.MessageToMessageEncoder.write(MessageToMessageEncoder.java:113) ~[netty-codec-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeWrite0(AbstractChannelHandlerContext.java:881) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.AbstractChannelHandlerContext.invokeWriteAndFlush(AbstractChannelHandlerContext.java:940) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:966) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:934) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:984) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.DefaultChannelPipeline.writeAndFlush(DefaultChannelPipeline.java:1025) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.AbstractChannel.writeAndFlush(AbstractChannel.java:306) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at ug.c(SourceFile:321) ~[server-1.20.4.jar:?]
        at ug.d(SourceFile:316) ~[server-1.20.4.jar:?]
        at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569) ~[netty-transport-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.97.Final.jar:4.1.97.Final]
        at java.lang.Thread.run(Thread.java:840) ~[?:?]

Code analysis:

...
public class ClientboundLoginDisconnectPacket implements Packet<ClientLoginPacketListener> {

    ...

    public ClientboundLoginDisconnectPacket(FriendlyByteBuf buf) {
        this.reason = Component.Serializer.fromJsonLenient(buf.readUtf(FriendlyByteBuf.MAX_COMPONENT_STRING_LENGTH));
    }

    @Override
    public void write(FriendlyByteBuf buf) {
        //buf.writeUtf(Component.Serializer.toJson(this.reason));

        // Set the maximum component length correctly.
        buf.writeUtf(Component.Serializer.toJson(this.reason), FriendlyByteBuf.MAX_COMPONENT_STRING_LENGTH);
    }
...

Comments 2

Is it intended that the number of characters mentioned in the "How to Reproduce" part is different to 32767?

Ban reason is converted to JSON component format when sent to the player.

The converted ban reason always has the following format. (64 number of characters)

{"translate":"multiplayer.disconnect.banned.reason","with":[""]}

To trigger the error, it needs to exceed 32767 characters(32768+), so 32704 characters or more is correct.

LemonCaramel

(Unassigned)

Confirmed

Platform

Low

Dedicated Server

23w42a, 1.20.4, 23w51b, 1.21.1, 24w37a

Retrieved