-- 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 hostname = "host" local counter = 0 local limits 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) local func, err = comms.call("supple:loadstring", "__call", codestr, codename) if not func then error(err) end -- Clear 'err' because it'll be the environment table for the function -- which we do not need to fill out. err = nil 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 if limitsok then 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 -- And ask the supple API to clear down too objects.clean_down(true) comms._set_fd(-1) luxio.kill(child.pid, luxio.SIGKILL) child:wait() if ok then return ok, unpack(ret) else return ok, err end end local function set_hostname(newname) hostname = newname counter = 0 end local function set_limits(newlimits) limits = newlimits end return { run = run_sandbox, set_name = set_hostname, set_limits = set_limits, }