From 46d462b83e74d07735af05ee352844d02906b4a7 Mon Sep 17 00:00:00 2001 From: Shane Freeder Date: Fri, 12 Apr 2024 20:37:00 +0100 Subject: [PATCH] Fix StackOverflowException thrown on shutdown (Fixes #10404) (#10408) paper previously migrated away from using executeIfPossible as this throws a RejectedExecutionException when the server is shutting down, which is then picked up by the Connection handler object and causes the player to be kicked without the intended disconnection message that comes from commands such as /stop, /restart This was fine, because previously changes made in spigot would just prevent these packets from being executed anyways. Instead, we'll just use a marker exception to try to detect this specific state. --- .../0818-Fix-player-kick-on-shutdown.patch | 23 ------- ...x-premature-player-kicks-on-shutdown.patch | 61 +++++++++++++++++++ ...-Manager-and-add-advanced-packet-sup.patch | 20 +++--- ...ocity-compression-and-cipher-natives.patch | 6 +- ...l-more-information-in-watchdog-dumps.patch | 8 +-- 5 files changed, 78 insertions(+), 40 deletions(-) delete mode 100644 patches/server/0818-Fix-player-kick-on-shutdown.patch create mode 100644 patches/server/0818-Fix-premature-player-kicks-on-shutdown.patch diff --git a/patches/server/0818-Fix-player-kick-on-shutdown.patch b/patches/server/0818-Fix-player-kick-on-shutdown.patch deleted file mode 100644 index 6b694a6abe..0000000000 --- a/patches/server/0818-Fix-player-kick-on-shutdown.patch +++ /dev/null @@ -1,23 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Denery -Date: Sun, 6 Nov 2022 02:02:46 +0300 -Subject: [PATCH] Fix player kick on shutdown - -Fix preemptive player kick on a server shutdown. -If you update minecraft version / upstream and something is changed in this method make sure that a server doesn't disconnect a player preemptively, -also check if all packets are ignored during the shutdown process. -See net.minecraft.network.Connection#channelRead0(ChannelHandlerContext, Packet) and net.minecraft.util.thread.BlockableEventLoop#executeIfPossible(Runnable) - -diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java -index b7ffab0284b0bccd79775b8d03c8b2e088f91d1d..4202c48ad8f8e85ef46d1bd446ab28f3f4b083d1 100644 ---- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java -+++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java -@@ -26,7 +26,7 @@ public class PacketUtils { - - public static void ensureRunningOnSameThread(Packet packet, T listener, BlockableEventLoop engine) throws RunningOnDifferentThreadException { - if (!engine.isSameThread()) { -- engine.executeIfPossible(() -> { -+ engine.execute(() -> { // Paper - Fix preemptive player kick on a server shutdown - if (listener instanceof ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // CraftBukkit - Don't handle sync packets for kicked players - if (listener.shouldHandleMessage(packet)) { - co.aikar.timings.Timing timing = co.aikar.timings.MinecraftTimings.getPacketTiming(packet); // Paper - timings diff --git a/patches/server/0818-Fix-premature-player-kicks-on-shutdown.patch b/patches/server/0818-Fix-premature-player-kicks-on-shutdown.patch new file mode 100644 index 0000000000..983dbfd32a --- /dev/null +++ b/patches/server/0818-Fix-premature-player-kicks-on-shutdown.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shane Freeder +Date: Thu, 11 Apr 2024 16:37:44 +0100 +Subject: [PATCH] Fix premature player kicks on shutdown + +When the server is stopping, the default execution handler method will throw a +RejectedExecutionException in order to prevent further execution, this causes +us to lose the actual kick reason. To mitigate this, we'll use a seperate marked +class in order to gracefully ignore these. + +diff --git a/src/main/java/io/papermc/paper/util/ServerStopRejectedExecutionException.java b/src/main/java/io/papermc/paper/util/ServerStopRejectedExecutionException.java +new file mode 100644 +index 0000000000000000000000000000000000000000..2c5cd77103c5a33d4349ab6b9ee2d8378bb60eb4 +--- /dev/null ++++ b/src/main/java/io/papermc/paper/util/ServerStopRejectedExecutionException.java +@@ -0,0 +1,20 @@ ++package io.papermc.paper.util; ++ ++import java.util.concurrent.RejectedExecutionException; ++ ++public class ServerStopRejectedExecutionException extends RejectedExecutionException { ++ public ServerStopRejectedExecutionException() { ++ } ++ ++ public ServerStopRejectedExecutionException(final String message) { ++ super(message); ++ } ++ ++ public ServerStopRejectedExecutionException(final String message, final Throwable cause) { ++ super(message, cause); ++ } ++ ++ public ServerStopRejectedExecutionException(final Throwable cause) { ++ super(cause); ++ } ++} +diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java +index 22a7f17180b76b6c3548d3b54ae8218a469401a8..c399625a342ffd61102bb96a97ac24b0669e8e17 100644 +--- a/src/main/java/net/minecraft/network/Connection.java ++++ b/src/main/java/net/minecraft/network/Connection.java +@@ -290,6 +290,7 @@ public class Connection extends SimpleChannelInboundHandler> { + Connection.genericsFtw(packet, packetlistener); + } catch (RunningOnDifferentThreadException cancelledpackethandleexception) { + ; ++ } catch (io.papermc.paper.util.ServerStopRejectedExecutionException ignored) { // Paper - do not prematurely disconnect players on stop + } catch (RejectedExecutionException rejectedexecutionexception) { + this.disconnect(Component.translatable("multiplayer.disconnect.server_shutdown")); + } catch (ClassCastException classcastexception) { +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index bfee202e1dc8ea875b9d2b4e8c3b0be3f6d94b26..d50d3bc9c0f573cdb43100bce6e3dbfe2102fc53 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -2013,7 +2013,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop> { @@ -51,7 +51,7 @@ index 22a7f17180b76b6c3548d3b54ae8218a469401a8..7f2aa5e17fe675f3404d67b1794d2ca6 // Paper start - add utility methods public final net.minecraft.server.level.ServerPlayer getPlayer() { -@@ -375,15 +379,39 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -376,15 +380,39 @@ public class Connection extends SimpleChannelInboundHandler> { } public void send(Packet packet, @Nullable PacketSendListener callbacks, boolean flush) { @@ -97,7 +97,7 @@ index 22a7f17180b76b6c3548d3b54ae8218a469401a8..7f2aa5e17fe675f3404d67b1794d2ca6 } public void runOnceConnected(Consumer task) { -@@ -391,7 +419,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -392,7 +420,7 @@ public class Connection extends SimpleChannelInboundHandler> { this.flushQueue(); task.accept(this); } else { @@ -106,7 +106,7 @@ index 22a7f17180b76b6c3548d3b54ae8218a469401a8..7f2aa5e17fe675f3404d67b1794d2ca6 } } -@@ -409,6 +437,14 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -410,6 +438,14 @@ public class Connection extends SimpleChannelInboundHandler> { } private void doSendPacket(Packet packet, @Nullable PacketSendListener callbacks, boolean flush) { @@ -121,7 +121,7 @@ index 22a7f17180b76b6c3548d3b54ae8218a469401a8..7f2aa5e17fe675f3404d67b1794d2ca6 ChannelFuture channelfuture = flush ? this.channel.writeAndFlush(packet) : this.channel.write(packet); if (callbacks != null) { -@@ -428,14 +464,24 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -429,14 +465,24 @@ public class Connection extends SimpleChannelInboundHandler> { }); } @@ -147,7 +147,7 @@ index 22a7f17180b76b6c3548d3b54ae8218a469401a8..7f2aa5e17fe675f3404d67b1794d2ca6 } } -@@ -468,20 +514,57 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -469,20 +515,57 @@ public class Connection extends SimpleChannelInboundHandler> { return attributekey; } @@ -212,7 +212,7 @@ index 22a7f17180b76b6c3548d3b54ae8218a469401a8..7f2aa5e17fe675f3404d67b1794d2ca6 private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world private static int joinAttemptsThisTick; // Paper - Buffer joins to world -@@ -544,6 +627,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -545,6 +628,7 @@ public class Connection extends SimpleChannelInboundHandler> { public void disconnect(Component disconnectReason) { // Spigot Start this.preparing = false; @@ -220,7 +220,7 @@ index 22a7f17180b76b6c3548d3b54ae8218a469401a8..7f2aa5e17fe675f3404d67b1794d2ca6 // Spigot End if (this.channel == null) { this.delayedDisconnect = disconnectReason; -@@ -715,7 +799,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -716,7 +800,7 @@ public class Connection extends SimpleChannelInboundHandler> { public void handleDisconnection() { if (this.channel != null && !this.channel.isOpen()) { if (this.disconnectionHandled) { @@ -229,7 +229,7 @@ index 22a7f17180b76b6c3548d3b54ae8218a469401a8..7f2aa5e17fe675f3404d67b1794d2ca6 } else { this.disconnectionHandled = true; PacketListener packetlistener = this.getPacketListener(); -@@ -728,7 +812,7 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -729,7 +813,7 @@ public class Connection extends SimpleChannelInboundHandler> { packetlistener1.onDisconnect(ichatbasecomponent); } @@ -238,7 +238,7 @@ index 22a7f17180b76b6c3548d3b54ae8218a469401a8..7f2aa5e17fe675f3404d67b1794d2ca6 // Paper start - Add PlayerConnectionCloseEvent final PacketListener packetListener = this.getPacketListener(); if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) { -@@ -765,4 +849,93 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -766,4 +850,93 @@ public class Connection extends SimpleChannelInboundHandler> { public void setBandwidthLogger(SampleLogger log) { this.bandwidthDebugMonitor = new BandwidthDebugMonitor(log); } diff --git a/patches/server/1013-Use-Velocity-compression-and-cipher-natives.patch b/patches/server/1013-Use-Velocity-compression-and-cipher-natives.patch index a9cc979072..8ba0372a99 100644 --- a/patches/server/1013-Use-Velocity-compression-and-cipher-natives.patch +++ b/patches/server/1013-Use-Velocity-compression-and-cipher-natives.patch @@ -260,10 +260,10 @@ index 11a466558c77b43969b8e4be3a3470f84c7fcb1a..ae6e8ab9c1afa31d808f1fce2654a8b9 } diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java -index 7f2aa5e17fe675f3404d67b1794d2ca68b188eb9..fff375fd50fa1a804636a92ded1ae55cff42977d 100644 +index 16eb94eb1f40485daef2713f740f6e0beeb1463f..fae2a57570a4007b67b9949b9b16504da36a9886 100644 --- a/src/main/java/net/minecraft/network/Connection.java +++ b/src/main/java/net/minecraft/network/Connection.java -@@ -734,11 +734,28 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -735,11 +735,28 @@ public class Connection extends SimpleChannelInboundHandler> { return networkmanager; } @@ -296,7 +296,7 @@ index 7f2aa5e17fe675f3404d67b1794d2ca68b188eb9..fff375fd50fa1a804636a92ded1ae55c public boolean isEncrypted() { return this.encrypted; -@@ -771,16 +788,17 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -772,16 +789,17 @@ public class Connection extends SimpleChannelInboundHandler> { public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) { if (compressionThreshold >= 0) { diff --git a/patches/server/1014-Detail-more-information-in-watchdog-dumps.patch b/patches/server/1014-Detail-more-information-in-watchdog-dumps.patch index 2dce8a04c2..e403a57375 100644 --- a/patches/server/1014-Detail-more-information-in-watchdog-dumps.patch +++ b/patches/server/1014-Detail-more-information-in-watchdog-dumps.patch @@ -7,10 +7,10 @@ Subject: [PATCH] Detail more information in watchdog dumps - Dump player name, player uuid, position, and world for packet handling diff --git a/src/main/java/net/minecraft/network/Connection.java b/src/main/java/net/minecraft/network/Connection.java -index fff375fd50fa1a804636a92ded1ae55cff42977d..4716f8bd8a64d4f20f0d5957c1e7fabf63020f43 100644 +index fae2a57570a4007b67b9949b9b16504da36a9886..a536ebcf29d8ef0ed32863bd8d5e70f7a0636e8d 100644 --- a/src/main/java/net/minecraft/network/Connection.java +++ b/src/main/java/net/minecraft/network/Connection.java -@@ -586,7 +586,13 @@ public class Connection extends SimpleChannelInboundHandler> { +@@ -587,7 +587,13 @@ public class Connection extends SimpleChannelInboundHandler> { if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener) || loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING || Connection.joinAttemptsThisTick++ < MAX_PER_TICK) { @@ -25,7 +25,7 @@ index fff375fd50fa1a804636a92ded1ae55cff42977d..4716f8bd8a64d4f20f0d5957c1e7fabf // Paper end - Buffer joins to world } diff --git a/src/main/java/net/minecraft/network/protocol/PacketUtils.java b/src/main/java/net/minecraft/network/protocol/PacketUtils.java -index 4202c48ad8f8e85ef46d1bd446ab28f3f4b083d1..32838f87978c0694bdb573236b7cdf72b2e363cd 100644 +index b7ffab0284b0bccd79775b8d03c8b2e088f91d1d..83302c252f54481f239522e5c6861ccfe233070a 100644 --- a/src/main/java/net/minecraft/network/protocol/PacketUtils.java +++ b/src/main/java/net/minecraft/network/protocol/PacketUtils.java @@ -18,6 +18,24 @@ public class PacketUtils { @@ -56,7 +56,7 @@ index 4202c48ad8f8e85ef46d1bd446ab28f3f4b083d1..32838f87978c0694bdb573236b7cdf72 @@ -27,6 +45,8 @@ public class PacketUtils { public static void ensureRunningOnSameThread(Packet packet, T listener, BlockableEventLoop engine) throws RunningOnDifferentThreadException { if (!engine.isSameThread()) { - engine.execute(() -> { // Paper - Fix preemptive player kick on a server shutdown + engine.executeIfPossible(() -> { + packetProcessing.push(listener); // Paper - detailed watchdog information + try { // Paper - detailed watchdog information if (listener instanceof ServerCommonPacketListenerImpl serverCommonPacketListener && serverCommonPacketListener.processedDisconnect) return; // CraftBukkit - Don't handle sync packets for kicked players