diff --git a/mesecons_luacontroller/init.lua b/mesecons_luacontroller/init.lua index 19b6479..1c93e48 100644 --- a/mesecons_luacontroller/init.lua +++ b/mesecons_luacontroller/init.lua @@ -266,32 +266,45 @@ local function remove_functions(x) return x end --- itbl: Flat table of functions to run after sandbox cleanup, used to prevent various security hazards -local function get_interrupt(pos, itbl, send_warning) - -- iid = interrupt id - local function interrupt(time, iid) - -- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y - -- Hence the values get moved out. Should take less time than original, so totally compatible - if type(time) ~= "number" then return end - table.insert(itbl, function () - -- Outside string metatable sandbox, can safely run this now - local luac_id = minetest.get_meta(pos):get_int("luac_id") - -- Check if IID is dodgy, so you can't use interrupts to store an infinite amount of data. - -- Note that this is safe from alter-after-free because this code gets run after the sandbox has ended. - -- This runs outside of the timer and *shouldn't* harm perf. unless dodgy data is being sent in the first place - iid = remove_functions(iid) - local msg_ser = minetest.serialize(iid) - if #msg_ser <= mesecon.setting("luacontroller_interruptid_maxlen", 256) then - mesecon.queue:add_action(pos, "lc_interrupt", {luac_id, iid}, time, iid, 1) - else - send_warning("An interrupt ID was too large!") - end +-- The setting affects API so is not intended to be changeable at runtime +local get_interrupt +if mesecon.setting("luacontroller_lightweight_interrupts", false) then + -- use node timer + get_interrupt = function(pos, itbl, send_warning) + return (function(time, iid) + if type(time) ~= "number" then error("Delay must be a number") end + if iid ~= nil then send_warning("Interrupt IDs are disabled on this server") end + table.insert(itbl, function() minetest.get_node_timer(pos):start(time) end) end) end - return interrupt +else + -- use global action queue + -- itbl: Flat table of functions to run after sandbox cleanup, used to prevent various security hazards + get_interrupt = function(pos, itbl, send_warning) + -- iid = interrupt id + local function interrupt(time, iid) + -- NOTE: This runs within string metatable sandbox, so don't *rely* on anything of the form (""):y + -- Hence the values get moved out. Should take less time than original, so totally compatible + if type(time) ~= "number" then error("Delay must be a number") end + table.insert(itbl, function () + -- Outside string metatable sandbox, can safely run this now + local luac_id = minetest.get_meta(pos):get_int("luac_id") + -- Check if IID is dodgy, so you can't use interrupts to store an infinite amount of data. + -- Note that this is safe from alter-after-free because this code gets run after the sandbox has ended. + -- This runs outside of the timer and *shouldn't* harm perf. unless dodgy data is being sent in the first place + iid = remove_functions(iid) + local msg_ser = minetest.serialize(iid) + if #msg_ser <= mesecon.setting("luacontroller_interruptid_maxlen", 256) then + mesecon.queue:add_action(pos, "lc_interrupt", {luac_id, iid}, time, iid, 1) + else + send_warning("An interrupt ID was too large!") + end + end) + end + return interrupt + end end - -- Given a message object passed to digiline_send, clean it up into a form -- which is safe to transmit over the network and compute its "cost" (a very -- rough estimate of its memory usage). @@ -414,7 +427,6 @@ local function get_digiline_send(pos, itbl, send_warning) end end - local safe_globals = { -- Don't add pcall/xpcall unless willing to deal with the consequences (unless very careful, incredibly likely to allow killing server indirectly) "assert", "error", "ipairs", "next", "pairs", "select", @@ -651,6 +663,14 @@ local function reset(pos) set_port_states(pos, {a=false, b=false, c=false, d=false}) end +local function node_timer(pos) + if minetest.registered_nodes[minetest.get_node(pos).name].is_burnt then + return false + end + run(pos, {type="interrupt"}) + return false +end + ----------------------- -- A.Queue callbacks -- ----------------------- @@ -823,6 +843,7 @@ for d = 0, 1 do mesecon.receptor_off(pos, output_rules) end, is_luacontroller = true, + on_timer = node_timer, on_blast = mesecon.on_blastnode, }) end diff --git a/settingtypes.txt b/settingtypes.txt index 8be7be6..9259a83 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -24,6 +24,10 @@ mesecon.luacontroller_digiline_maxlen (Digiline message size limit) int 50000 10 mesecon.luacontroller_maxevents (Controller execution time limit) int 10000 1000 100000 mesecon.luacontroller_memsize (Controller memory limit) int 100000 10000 1000000 +# Use node timer for interrupts (runs in active blocks only). +# IID is ignored and at most one interrupt may be queued if this setting is enabled. +mesecon.luacontroller_lightweight_interrupts (Lightweight interrupts) bool false + [mesecons_movestones]