-- lib/supple/host.lua -- -- Sandbox (for) Untrusted Procedure Partitioning (in) Lua Engine -- -- Management of the host side of Supple -- -- Copyright 2012 Daniel Silverstone -- -- For licence terms, see COPYING -- local luxio = require 'luxio' local subprocess = require 'luxio.subprocess' local comms = require 'supple.comms' local objects = require 'supple.objects' local capi = require 'supple.capi' local track = require 'supple.track' local hostname = "host" local counter = 0 local limits, globals local function simplify(t, memo) if not memo then memo = {} end if memo[t] then return memo[t] end local ret = {} memo[t] = ret local kk, vv for k, v in capi.pairs(t) do kk, vv = k, v if capi.type(k) == "table" then kk = simplify(k, memo) end if capi.type(v) == "table" then vv = simplify(v, memo) end if capi.rawtype(kk) ~= "userdata" and capi.rawtype(vv) ~= "userdata" then -- We've not got a proxy left over anywhere, so copy it ret[kk] = vv end end return ret end local function run_wrapper() local wrapperpath = "@@WRAPPER_BIN@@" -- START_TEST_SUPPLE wrapperpath = "./testwrapper" -- END_TEST_SUPPLE local fds = {} local ret, errno = luxio.socketpair(luxio.AF_UNIX, luxio.SOCK_STREAM, luxio.PF_UNIX, fds) if ret ~= 0 then error("Unable to launch subprocess, could not prepare socketpair():" .. luxio.strerror(errno)) end local proc, msg = subprocess.spawn { "supple-sandbox", exe = wrapperpath, stdin = fds[1], stdout = -1, stderr = -1, close_in_child = { fds[1], fds[2] }, } if not proc then error(msg) end luxio.close(fds[1]) return proc, fds[2] end local function run_sandbox(codestr, codename, ...) -- Prepare and start a sandbox, -- compiling the codestr and running it -- with the given args local child, commsfd = run_wrapper() counter = counter + 1 objects.set_name(("host[%d,%%d]"):format(counter)) comms._set_fd(commsfd) objects.set_proc_call(comms.call) track.start() local func, env = comms.call("supple:loadstring", "__call", codestr, codename) if not func then error(env) end local limitsok, limitserr = true if limits then limitsok, limitserr = comms.call("supple:set_limits", "__call", limits) end -- In a protected manner, capture the output of the call local args, ret = {...} local function capture() ret = {func(unpack(args))} end local ok, err if limitsok then if globals then -- Hand over the globals for k, v in pairs(globals) do env[k] = v end end ok, err = pcall(capture) else ok, err = limitsok, limitserr end -- Convert any complex objects returned to us, so we can clean up... if ok then ret = simplify(ret) end -- We need to clean up, so dump all the objects func = nil env = nil -- And ask the supple API to clear down too objects.clean_down(true) comms._set_fd(-1) luxio.close(commsfd) child:wait() if ok then return ok, unpack(ret) else return ok, err .. "\n\nHost comms track:\n" .. track.stop() end end local function sandboxed_loadstring(codestr, codename) return comms.call("supple:loadstring", "__call", codestr, codename) end local function set_hostname(newname) hostname = newname counter = 0 end local function set_limits(newlimits) limits = newlimits end local function set_globals(newglobals) globals = newglobals end return { run = run_sandbox, loadstring = sandboxed_loadstring, set_name = set_hostname, set_limits = set_limits, set_globals = set_globals, }