Skip to content

Commit

Permalink
LuaC: add lightweight interrupts (#449)
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasrudin authored and numberZero committed Dec 29, 2018
1 parent 302a289 commit 737f366
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 23 deletions.
67 changes: 44 additions & 23 deletions mesecons_luacontroller/init.lua
Expand Up @@ -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).
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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 --
-----------------------
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions settingtypes.txt
Expand Up @@ -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]

Expand Down

0 comments on commit 737f366

Please sign in to comment.