diff --git a/mods/mesecons/mesecons_luacontroller/init.lua b/mods/mesecons/mesecons_luacontroller/init.lua index 7df2bdc9..e669f937 100755 --- a/mods/mesecons/mesecons_luacontroller/init.lua +++ b/mods/mesecons/mesecons_luacontroller/init.lua @@ -216,6 +216,17 @@ local function safe_string_rep(str, n) return string.rep(str, n) end +-- string.find with a pattern can be used to DoS the server. +-- Therefore, limit string.find to patternless matching. +local function safe_string_find(...) + if (select(4, ...)) ~= true then + debug.sethook() -- Clear hook + error("string.find: 'plain' (fourth parameter) must always be true in a LuaController") + end + + return string.find(...) +end + local function remove_functions(x) local tp = type(x) if tp == "table" then @@ -292,6 +303,7 @@ local function create_environment(pos, mem, event) rep = safe_string_rep, reverse = string.reverse, sub = string.sub, + find = safe_string_find, }, math = { abs = math.abs, @@ -354,22 +366,6 @@ local function timeout() end -local function code_prohibited(code) - -- LuaJIT doesn't increment the instruction counter when running - -- loops, so we have to sanitize inputs if we're using LuaJIT. - if not rawget(_G, "jit") then - return false - end - local prohibited = {"while", "for", "repeat", "until", "goto"} - code = " "..code.." " - for _, p in ipairs(prohibited) do - if string.find(code, "[^%w_]"..p.."[^%w_]") then - return "Prohibited command: "..p - end - end -end - - local function create_sandbox(code, env) if code:byte(1) == 27 then return nil, "Binary code prohibited." @@ -378,24 +374,17 @@ local function create_sandbox(code, env) if not f then return nil, msg end setfenv(f, env) + -- Turn off JIT optimization for user code so that count + -- events are generated when adding debug hooks + if rawget(_G, "jit") then + jit.off(f, true) + end + return function(...) - -- Normal Lua: Use instruction counter to stop execution - -- after luacontroller_maxevents. - -- LuaJIT: Count function calls instead of instructions, allows usage - -- of function keyword. However, LuaJIT still doesn't trigger - -- lines events when using infinite loops. + -- Use instruction counter to stop execution + -- after luacontroller_maxevents local maxevents = mesecon.setting("luacontroller_maxevents", 10000) - if not rawget(_G, "jit") then - debug.sethook(timeout, "", maxevents) - else - local events = 0 - debug.sethook(function () - events = events + 1 - if events > maxevents then - timeout() - end - end, "c") - end + debug.sethook(timeout, "", maxevents) local ok, ret = pcall(f, ...) debug.sethook() -- Clear hook if not ok then error(ret, 0) end @@ -432,9 +421,6 @@ local function run(pos, event) local mem = load_memory(meta) local code = meta:get_string("code") - local err = code_prohibited(code) - if err then return err end - -- Create environment local env = create_environment(pos, mem, event)