From 23fb98647adb2bcf3db9247b6e345595671048be Mon Sep 17 00:00:00 2001 From: Raven Szewczyk Date: Sat, 21 Sep 2024 11:23:03 +0100 Subject: [PATCH 1/2] Limit recursive block updates to prevent stack overflow crashes --- .../hodgepodge/config/FixesConfig.java | 6 +++ .../mitchej123/hodgepodge/mixins/Mixins.java | 6 +++ ...MixinWorldServer_LimitUpdateRecursion.java | 44 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java diff --git a/src/main/java/com/mitchej123/hodgepodge/config/FixesConfig.java b/src/main/java/com/mitchej123/hodgepodge/config/FixesConfig.java index 7dd410b1..db7e2023 100644 --- a/src/main/java/com/mitchej123/hodgepodge/config/FixesConfig.java +++ b/src/main/java/com/mitchej123/hodgepodge/config/FixesConfig.java @@ -352,6 +352,12 @@ public class FixesConfig { @Config.RequiresMcRestart public static boolean fixCaseCommands; + @Config.Comment("Limit the number of recursive cascading block updates during world generation to prevent stack overflow crashes, set to -1 to disable the limit.") + @Config.RangeInt(min = -1) + @Config.DefaultInt(256) // A stack overflow with water updates happens somewhere above 300 updates with default Java + // settings + public static int limitRecursiveBlockUpdateDepth; + // affecting multiple mods @Config.Comment("Remove old/stale/outdated update checks.") diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java index c01aed89..7089dfab 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/Mixins.java @@ -421,6 +421,12 @@ public enum Mixins { .setPhase(Phase.EARLY).setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA) .addMixinClasses("minecraft.MixinCommandHandler_CommandFix").setApplyIf(() -> FixesConfig.fixCaseCommands)), + LIMIT_RECURSIVE_BLOCK_UPDATE_DEPTH(new Builder( + "Limit the number of recursive cascading block updates during world generation to prevent stack overflow crashes") + .setPhase(Phase.EARLY).setSide(Side.BOTH).addTargetedMod(TargetedMod.VANILLA) + .addMixinClasses("minecraft.MixinWorldServer_LimitUpdateRecursion") + .setApplyIf(() -> FixesConfig.limitRecursiveBlockUpdateDepth >= 0)), + // Ic2 adjustments IC2_UNPROTECTED_GET_BLOCK_FIX(new Builder("IC2 Kinetic Fix").setPhase(Phase.EARLY).setSide(Side.BOTH) .addMixinClasses("ic2.MixinIc2WaterKinetic").setApplyIf(() -> FixesConfig.fixIc2UnprotectedGetBlock) diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java new file mode 100644 index 00000000..befdeec0 --- /dev/null +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java @@ -0,0 +1,44 @@ +package com.mitchej123.hodgepodge.mixins.early.minecraft; + +import net.minecraft.block.Block; +import net.minecraft.world.WorldServer; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import com.mitchej123.hodgepodge.Common; +import com.mitchej123.hodgepodge.config.FixesConfig; + +@Mixin(WorldServer.class) +public class MixinWorldServer_LimitUpdateRecursion { + + public int hodgepodge$currentBlockUpdateRecursiveCalls = 0; + + @Inject(method = "scheduleBlockUpdateWithPriority", at = @At("HEAD"), cancellable = true) + void hodgepodge$incrementBlockUpdateRecursionCounter(int x, int y, int z, Block block, int tickDelay, int priority, + CallbackInfo ci) { + if (hodgepodge$currentBlockUpdateRecursiveCalls >= FixesConfig.limitRecursiveBlockUpdateDepth) { + final StackOverflowError error = new StackOverflowError( + String.format( + "Too many recursive block updates (%d) at world %d, block %s (%d, %d, %d) - aborting further block updates", + hodgepodge$currentBlockUpdateRecursiveCalls, + ((WorldServer) (Object) this).provider.dimensionId, + block, + x, + y, + z)); + Common.log.error(error.getMessage(), error); + ci.cancel(); + return; + } + hodgepodge$currentBlockUpdateRecursiveCalls++; + } + + @Inject(method = "scheduleBlockUpdateWithPriority", at = @At("RETURN")) + void hodgepodge$decrementBlockUpdateRecursionCounter(int x, int y, int z, Block block, int tickDelay, int priority, + CallbackInfo ci) { + hodgepodge$currentBlockUpdateRecursiveCalls = Math.max(0, hodgepodge$currentBlockUpdateRecursiveCalls - 1); + } +} From 6d7584ddacdb6345f76be912ba9c174941c8262a Mon Sep 17 00:00:00 2001 From: Raven Szewczyk Date: Sat, 21 Sep 2024 12:02:43 +0100 Subject: [PATCH 2/2] Review comments --- .../MixinWorldServer_LimitUpdateRecursion.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java index befdeec0..7087ed30 100644 --- a/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java +++ b/src/main/java/com/mitchej123/hodgepodge/mixins/early/minecraft/MixinWorldServer_LimitUpdateRecursion.java @@ -4,6 +4,7 @@ import net.minecraft.world.WorldServer; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -14,11 +15,12 @@ @Mixin(WorldServer.class) public class MixinWorldServer_LimitUpdateRecursion { - public int hodgepodge$currentBlockUpdateRecursiveCalls = 0; + @Unique + private int hodgepodge$currentBlockUpdateRecursiveCalls = 0; @Inject(method = "scheduleBlockUpdateWithPriority", at = @At("HEAD"), cancellable = true) - void hodgepodge$incrementBlockUpdateRecursionCounter(int x, int y, int z, Block block, int tickDelay, int priority, - CallbackInfo ci) { + private void hodgepodge$incrementBlockUpdateRecursionCounter(int x, int y, int z, Block block, int tickDelay, + int priority, CallbackInfo ci) { if (hodgepodge$currentBlockUpdateRecursiveCalls >= FixesConfig.limitRecursiveBlockUpdateDepth) { final StackOverflowError error = new StackOverflowError( String.format( @@ -37,8 +39,8 @@ public class MixinWorldServer_LimitUpdateRecursion { } @Inject(method = "scheduleBlockUpdateWithPriority", at = @At("RETURN")) - void hodgepodge$decrementBlockUpdateRecursionCounter(int x, int y, int z, Block block, int tickDelay, int priority, - CallbackInfo ci) { + private void hodgepodge$decrementBlockUpdateRecursionCounter(int x, int y, int z, Block block, int tickDelay, + int priority, CallbackInfo ci) { hodgepodge$currentBlockUpdateRecursiveCalls = Math.max(0, hodgepodge$currentBlockUpdateRecursiveCalls - 1); } }