From 4b91ae95228ebb2851c4828072d3af57f66b8aaa Mon Sep 17 00:00:00 2001 From: Nils Dagsson Moskopp Date: Tue, 15 Feb 2022 21:21:45 +0100 Subject: [PATCH] Fix C stack overflow on Nether portal destruction MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this patch, destroying an obsidian Nether portal frame destroyed Nether portal nodes recursively. In Minetest binaries compiled with Lua 5.1 (i.e. without LuaJIT) this would cause a stack overflow when a huge portal (23×23) was destroyed, crashing the server. This patch implements Nether portal destruction using node timers. When a portal node's timer triggers, it starts the timers of adjacent portal nodes with the same orientation and no active timer and deletes itself. Attempts to solve this problem using minetest.after() seemed promising, but rubenwardy pointed out that anything relying on minetest.after() is bound to fail if a server shuts down while portal nodes are destroyed. --- mods/ITEMS/mcl_portals/portal_nether.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/mods/ITEMS/mcl_portals/portal_nether.lua b/mods/ITEMS/mcl_portals/portal_nether.lua index 7e23a9f7..50258b40 100644 --- a/mods/ITEMS/mcl_portals/portal_nether.lua +++ b/mods/ITEMS/mcl_portals/portal_nether.lua @@ -76,8 +76,11 @@ local function destroy_nether_portal(pos) local check_remove = function(pos, orientation) local node = minetest.get_node(pos) if node and (node.name == "mcl_portals:portal" and (orientation == nil or (node.param2 == orientation))) then - minetest.log("action", "[mcl_portal] Destroying Nether portal at " .. minetest.pos_to_string(pos)) - return minetest.remove_node(pos) + local timer = minetest.get_node_timer(pos) + if not timer:is_started() then + minetest.log("action", "[mcl_portal] Queuing Nether portal for destruction at " .. minetest.pos_to_string(pos)) + timer:start(0.05) + end end end if obsidian then -- check each of 6 sides of it and destroy every portal: @@ -154,6 +157,10 @@ minetest.register_node("mcl_portals:portal", { }, groups = {portal=1, not_in_creative_inventory = 1}, on_destruct = destroy_nether_portal, + on_timer = function(pos, elapsed) + minetest.remove_node(pos) + return false + end, _mcl_hardness = -1, _mcl_blast_resistance = 0,