techpack/safer_lua/environ.lua

170 lines
3.9 KiB
Lua
Raw Normal View History

2018-06-24 22:33:00 +02:00
--[[
SaferLua [safer_lua]
====================
Copyright (C) 2018 Joachim Stolberg
LGPLv2.1+
See LICENSE.txt for more information
environ.lua:
]]--
safer_lua.MaxCodeSize = 2000 -- size in length of byte code
safer_lua.MaxTableSize = 1000 -- sum over all table sizes
2018-06-24 15:01:33 +02:00
local function memsize()
return safer_lua.MaxTableSize
end
2018-06-24 15:01:33 +02:00
local BASE_ENV = {
Array = safer_lua.Array,
2018-06-24 15:01:33 +02:00
Store = safer_lua.Store,
Set = safer_lua.Set,
memsize = memsize,
2018-06-24 15:01:33 +02:00
math = {
2018-06-25 20:42:47 +02:00
floor = math.floor,
abs = math.abs,
max = math.max,
min = math.min,
random = math.random,
2018-06-24 15:01:33 +02:00
},
2018-06-25 20:42:47 +02:00
tonumber = tonumber,
tostring = tostring,
type = type,
2018-06-24 15:01:33 +02:00
ticks = 0,
}
local function map(dest, source)
for k,v in pairs(source) do
dest[k] = v
end
return dest
end
local function calc_used_mem_size(env)
local size = 0
for key,val in pairs(env) do
if type(val) == "table" and val.size ~= nil then
size = size + val.size() or 0
end
end
return size
end
2018-06-24 15:01:33 +02:00
function safer_lua.config(max_code_size, max_table_size)
safer_lua.MaxCodeSize = max_code_size
safer_lua.MaxTableSize = max_table_size
end
2019-01-08 21:46:04 +01:00
local function format_error(err, tab)
err = err:gsub('%[string "%-%-.-"%]:', "in "..tab.." line ")
err = err:gsub('in main chunk.+', "")
err = err:gsub('%.%.%..-:%d+:', "Error")
return err
end
2018-06-24 15:01:33 +02:00
local function compile(pos, text, label, err_clbk)
if safer_lua:check(pos, text, label, err_clbk) == 0 then
2018-06-24 15:01:33 +02:00
text = text:gsub("%$", "S:")
local code, err = loadstring(text)
if not code then
err = err:gsub("%(load%):", label)
2018-06-24 15:01:33 +02:00
err_clbk(pos, err)
else
return code
end
end
end
-------------------------------------------------------------------------------
-- Standard init/loop controller
-------------------------------------------------------------------------------
2018-06-24 15:01:33 +02:00
function safer_lua.init(pos, init, loop, environ, err_clbk)
if #init > safer_lua.MaxCodeSize then
err_clbk(pos, "init() Code size limit exceeded")
return
end
if #loop > safer_lua.MaxCodeSize then
err_clbk(pos, "loop() Code size limit exceeded")
return
end
local code = compile(pos, init, "init() ", err_clbk)
if code then
local env = table.copy(BASE_ENV)
2018-06-24 15:01:33 +02:00
env.S = {}
env.S = map(env.S, environ)
setfenv(code, env)
2019-01-08 21:46:04 +01:00
local res, err = xpcall(code, debug.traceback)
2018-06-24 15:01:33 +02:00
if not res then
2019-01-08 21:46:04 +01:00
err_clbk(pos, format_error(err, "init()"))
2018-06-24 15:01:33 +02:00
else
env = getfenv(code)
code = compile(pos, loop, "loop() ", err_clbk)
if code then
setfenv(code, env)
return code
end
end
end
end
function safer_lua.run_loop(pos, elapsed, code, err_clbk)
local env = getfenv(code)
env.elapsed = elapsed
if elapsed < 0 then -- event?
env.event = true
else
env.event = false
env.ticks = env.ticks + 1
end
2019-01-08 21:46:04 +01:00
local res, err = xpcall(code, debug.traceback)
if calc_used_mem_size(env) > safer_lua.MaxTableSize then
err_clbk(pos, "Memory limit exceeded")
2018-06-24 15:01:33 +02:00
return false
end
if not res then
2019-01-08 21:46:04 +01:00
err_clbk(pos, format_error(err, "loop()"))
2018-06-24 15:01:33 +02:00
return false
end
return true
end
-------------------------------------------------------------------------------
-- Endless/Coroutine controller
-------------------------------------------------------------------------------
local function thread(pos, code, err_clbk)
while true do
local res, err = pcall(code)
if not res then
err = err:gsub("%[string .+%]:", "loop() ")
err_clbk(pos, err)
return false
end
local env = getfenv(code)
if calc_used_mem_size(env) > safer_lua.MaxTableSize then
err_clbk(pos, "Memory limit exceeded")
return false
end
coroutine.yield()
end
end
function safer_lua.co_create(pos, init, loop, environ, err_clbk)
local code = safer_lua.init(pos, init, loop, environ, err_clbk)
return coroutine.create(thread), code
end
function safer_lua.co_resume(pos, co, code, err_clbk)
local res, err = coroutine.resume(co, pos, code, err_clbk)
if not res then
err = err:gsub("%[string .+%]:", "loop() ")
err_clbk(pos, err)
return false
end
return true
end