Protect server against Luacontroller memory usage

This commit is contained in:
Jude Melton-Houghton 2022-02-03 22:30:12 -05:00
parent 4c5b13a347
commit 622b82cfd6
2 changed files with 42 additions and 4 deletions

View File

@ -590,8 +590,12 @@ local function load_memory(meta)
end
local function save_memory(pos, meta, mem)
local memstring = minetest.serialize(remove_functions(mem))
local function serialize_memory(mem)
return minetest.serialize(remove_functions(mem))
end
local function save_serialized_memory(pos, meta, memstring)
local memsize_max = mesecon.setting("luacontroller_memsize", 100000)
if (#memstring <= memsize_max) then
@ -629,6 +633,8 @@ local function run_inner(pos, code, event)
-- Create the sandbox and execute code
local f, msg = create_sandbox(code, env)
if not f then return false, msg end
local mem_use_limit = collectgarbage("count") + mesecon.setting("luacontroller_volatile_mem_limit", 2000)
local oom_msg = "not enough memory" -- Used to detect OOM errors, kind of a hack.
-- Start string true sandboxing
local onetruestring = getmetatable("")
-- If a string sandbox is already up yet inconsistent, something is very wrong
@ -637,16 +643,43 @@ local function run_inner(pos, code, event)
local success, msg = pcall(f)
onetruestring.__index = string
-- End string true sandboxing
if not success then return false, msg end
if collectgarbage("count") > mem_use_limit then
-- Volatile memory limit exceeded.
success = false
msg = oom_msg
end
-- Serialize the persistent memory.
local memstring
if success then
success, memstring = pcall(serialize_memory, env.mem)
if not success then
msg = memstring
-- Rethrow non-OOM errors.
if msg ~= oom_msg then error(msg, 0) end
end
end
if not success then
-- Handle OOM errors by collecting garbage.
if msg == oom_msg then
env, itbl, mem = nil -- Let the memory be collected.
collectgarbage()
print("Error: Luacontroller at " .. minetest.pos_to_string(pos)
.. " exhausted its available volatile memory. Controller overheats.")
burn_controller(pos)
end
return false, msg
end
if type(env.port) ~= "table" then
return false, "Ports set are invalid."
end
local success, memstring = pcall(serialize_memory, env.mem)
if not success then return false, memstring end -- memstring is the error message here.
-- Actually set the ports
set_port_states(pos, env.port)
-- Save memory. This may burn the luacontroller if a memory overflow occurs.
save_memory(pos, meta, env.mem)
save_serialized_memory(pos, meta, memstring)
-- Execute deferred tasks
for _, v in ipairs(itbl) do

View File

@ -24,6 +24,11 @@ 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
# The amount of server memory a controller is allowed, measured in kilobytes.
# This actually measures the memory usage after the script is done, not during its runtime.
# Thus, controllers can lag the server even with a low limit.
mesecon.luacontroller_volatile_mem_limit (Controller volatile memory limit) int 2000 10 10000000
# 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