Repository navigation
reenterConfiguration can send block_changed_ack in the configuration protocol and crash the server #14119
Description
Activity
entering reconfiguration when the server is processing a players packet and primed to send them data is going to cause issues, I don't think it's really on our plate to try to mitigate that, if you want to enter configuration you'll need to do it at a sane time.
There is no pending play packets, those packets are sent right after the event is fired as part of the our and mojangs standard interaction logic, neither we nor mojang expect you to enter reconfiguration there
What do you define as a "sane time"? Theoretically a player could always be breaking a blocking when calling this API. I mean its minecraft overall
Or is it expected to basically freeze all player interactions before using this?
Thanks for the clarification. I understand that the minimal reproducer calls reenterConfiguration() re-entrantly while the server is still processing the ServerboundPlayerActionPacket, and that the block change acknowledgement is created after BlockBreakEvent returns.
However, I think there are still two separate concerns here.
First, the public API does not document this precondition. The Javadocs for PlayerGameConnection#reenterConfiguration() only state that it moves the player into the configuration stage:
There is no documented definition of a "sane time", nor does the public API provide a way to determine whether the connection is currently processing an interaction or is about to send a PLAY packet.
Moving the call into a delayed scheduler task only makes the issue less likely. In my original testing, it could still happen when the player happened to interact with or break another block shortly before the delayed task entered configuration. A plugin cannot atomically ensure that no new player action is processed between choosing to enter configuration and the actual protocol transition.
What is the supported way for a plugin to guarantee a safe transition? For example, is running it one tick later considered safe? If so, how should the plugin handle the player sending another block interaction before that task executes?
Second, even if calling the method from BlockBreakEvent is considered invalid usage, I do not think an invalid invocation of this public API should be able to crash the entire server. Paper could reject the call with an exception, defer the transition, or disconnect the affected player. Instead, after the encoder failure, disconnect cleanup removes the player a second time and EntityScheduler#retire() throws IllegalStateException: Already retired from the main server tick.
The API around some of this stuff is still new and with the expectation that you understand the underlying concepts of what is going on, that part is somewhat unavoidable around this sort of low-level API, any gating here would likely be fairly obtuse in terms of scope and I'm not sure how we'd we'd want that to cover
it not working safely from the scheduler sounds like a flaw in mojangs behavior here, paper is mostly just deferring to the vanilla logic here, I would presume that they'd be handling packets already on the wire with their entire configuration handshake
invoking API generally shouldn't cause crashes but with API at this low level of interaction, it gets a bit harsh we're exposing the ability to put clients into states which aren't exactly battle-tested in Mojang land
But then you can never actually use this API because it could crash the server at any time, right? That can't be right. Even if it's low-level.
That would look to be the case, mojang doesn't even use this mechanism outside of a debug command so they'd never have themselves in such a case of having player interactions around stuff occurring, I guess you'd probably need to litter a dozen "are they still in the world" checks around the incoming side, last I knew those checks weren't as strong as they should be for various reasons
Okay, then I guess we can't use the API, unfortunately. It looks like Folia has at least fixed the server crash (0001-Region-Threading-Base.patch, I think). In any case, the server doesn't crash with Folia—the player just gets disconnected. Maybe there could at least be a fix for the server crash.
Here is a log from Folia where the server didn't crash: https://mclo.gs/Aj1bN1S
I thinl this can be closed @twisti-dev
Resolved by #14346
Metadata
Metadata
Assignees
Labels
Type
Fields
MC Issue
Stack trace
Crash-Report: https://mclo.gs/MYzXc1J
Log: https://mclo.gs/e55gsTg
Plugin and Datapack List
Actions to reproduce (if known)
Player#getConnection().reenterConfiguration()fromBlockBreakEvent.PlayerConnectionReconfigureEventis fired, the plugin immediately callscompleteReconfiguration().clientbound/minecraft:block_changed_ackafter the outbound connection has entered the configuration protocol.IllegalStateException: Already retired. This exception can terminate the server.Minimal reproduction code:
Paper version
Other
Expected behavior:
Calling
reenterConfiguration()should safely transition the player into the configuration phase, defer the transition until pending PLAY packets have been handled, or reject the call without corrupting the connection state.It should not encode a PLAY packet using the CONFIGURATION protocol, disconnect the player, or crash the server.
Observed behavior:
The first relevant error is:
The player is then disconnected. During disconnect cleanup, Paper attempts to remove the player again even though the player was already removed while switching to the configuration phase. This produces:
The minimal reproduction invokes
reenterConfiguration()directly fromBlockBreakEventbecause this makes the timing easy to reproduce.However, the issue does not appear to be inherently specific to
BlockBreakEvent. It can also occur whenreenterConfiguration()is invoked from another task or event while the player happens to be breaking or interacting with a block and a block change acknowledgement is pending at the time of the protocol transition.Calling the method after a fixed delay reduces the likelihood, but does not eliminate the problem if the player continues interacting with blocks during that delay.