diff options
author | Daniel Silverstone <daniel.silverstone@codethink.co.uk> | 2012-08-15 15:46:50 +0100 |
---|---|---|
committer | Daniel Silverstone <daniel.silverstone@codethink.co.uk> | 2012-08-15 15:46:50 +0100 |
commit | 707b69c100d503c045f666c48663ec260d1c6920 (patch) | |
tree | 164e84078c48ada8e8a280595f2f0971e9adf75d /lib | |
parent | 4515790d34ff0a955dfc24d1db075e08f71cef62 (diff) | |
parent | 54fe3def54132bf739f38cd77ad78a73aa6ad23b (diff) | |
download | supple-707b69c100d503c045f666c48663ec260d1c6920.tar.gz |
Merge branch 'diagnosis'
Diffstat (limited to 'lib')
-rw-r--r-- | lib/supple.lua | 2 | ||||
-rw-r--r-- | lib/supple/capi.c | 2 | ||||
-rw-r--r-- | lib/supple/comms.lua | 29 | ||||
-rw-r--r-- | lib/supple/host.lua | 7 | ||||
-rw-r--r-- | lib/supple/objects.lua | 49 | ||||
-rw-r--r-- | lib/supple/sandbox.lua | 14 | ||||
-rw-r--r-- | lib/supple/track.lua | 62 |
7 files changed, 154 insertions, 11 deletions
diff --git a/lib/supple.lua b/lib/supple.lua index 9cb2d8a..513d3ac 100644 --- a/lib/supple.lua +++ b/lib/supple.lua @@ -13,6 +13,7 @@ local objects = require 'supple.objects' local comms = require 'supple.comms' local sandbox = require 'supple.sandbox' local host = require 'supple.host' +local track = require 'supple.track' local _VERSION = 1 local _ABI = 1 @@ -26,6 +27,7 @@ return { comms = comms, sandbox = sandbox, host = host, + track = track, _VERSION = _VERSION, VERSION = VERSION, _ABI = _ABI, diff --git a/lib/supple/capi.c b/lib/supple/capi.c index 38c4f27..34e6169 100644 --- a/lib/supple/capi.c +++ b/lib/supple/capi.c @@ -181,7 +181,7 @@ supple_capi_lockdown(lua_State *L) char *tempdir; struct rlimit lim; lua_settop(L, 0); /* Discard any arguments */ - + /* Prepare a copy of the tempdir template ready */ tempdir = lua_newuserdata(L, strlen(TEMPDIR_NAME)); strcpy(tempdir, TEMPDIR_NAME); diff --git a/lib/supple/comms.lua b/lib/supple/comms.lua index e72f8f1..ae2779c 100644 --- a/lib/supple/comms.lua +++ b/lib/supple/comms.lua @@ -14,6 +14,7 @@ local luxio = require "luxio" local capi = require "supple.capi" local request = require "supple.request" local objects = require "supple.objects" +local track = require "supple.track" local unpack = unpack local tonumber = tonumber @@ -34,6 +35,7 @@ local function send_msg(msg) error("Message too long") end local msglen = ("%05d"):format(#msg) + track.record("XMIT", msg) luxio.write(fd, msglen .. msg) end @@ -53,6 +55,7 @@ local function recv_msg() if type(str) ~= "string" or #str ~= len then error("Unable to read " .. tostring(len) .. " bytes of msg") end + track.record("RECV", str) return str end @@ -64,7 +67,8 @@ local function captcha(msg) return info.short_src:match("/supple/[^%.]+%.lua$") end while info and info.func ~= xpcall do - if info.currentline > 0 and not in_supple() then +-- if info.currentline > 0 and not in_supple() then + if true then local ttype, tag = objects.find_tag(info.func) if ttype then info.name = tag @@ -78,7 +82,7 @@ local function captcha(msg) level = level + 1 info = getinfo(level, "Snlf") end - return {msg, concat(traceback, "\n") or ""} + return {msg, (concat(traceback, "\n") or "") .. "\nComms track:\n" .. track.stop()} end local limits = {} @@ -111,7 +115,23 @@ end local function wait_for_response() repeat + -- We *MUST* disable GC for the duration of receiving and parsing the + -- message, lest we send a __gc transaction between the first and second + -- read operations. Or indeed if we GC and object between receiving the + -- message and deserialising and thus anchoring the object. If we error + -- out here, we don't really care about restarting gc since we won't + -- survive long anyhow. + gc "stop" + -- Also anchor all the objects just in case + local anchor = objects.get_object_anchor() + -- Now receive and deserialise (thus anchoring relevant objects) local back = request.deserialise(recv_msg()) + -- Now that the request is anchored, release the extra anchor and + -- re-allow GC + anchor = nil + gc "restart" + -- And get on with parsing the message we received.. + -- back could be three things -- an error (raise it) if back.error then @@ -182,9 +202,12 @@ local function wait_for_response() end local function make_call(object, method, ...) + track.enter("make_call", object, method) local req = request.request(object, method, ...) send_msg(req) - return wait_for_response() + local ret = {wait_for_response()} + track.leave("make_call", object, method) + return unpack(ret) end local function set_limits(newlimits) diff --git a/lib/supple/host.lua b/lib/supple/host.lua index 617b365..596fba1 100644 --- a/lib/supple/host.lua +++ b/lib/supple/host.lua @@ -15,6 +15,7 @@ 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 @@ -80,6 +81,8 @@ local function run_sandbox(codestr, codename, ...) 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 @@ -120,12 +123,12 @@ local function run_sandbox(codestr, codename, ...) objects.clean_down(true) comms._set_fd(-1) - luxio.kill(child.pid, luxio.SIGKILL) + luxio.close(commsfd) child:wait() if ok then return ok, unpack(ret) else - return ok, err + return ok, err .. "\n\nHost comms track:\n" .. track.stop() end end diff --git a/lib/supple/objects.lua b/lib/supple/objects.lua index 5a5dfca..2f5da79 100644 --- a/lib/supple/objects.lua +++ b/lib/supple/objects.lua @@ -12,18 +12,22 @@ local gc = collectgarbage local capi = require 'supple.capi' +local track = require 'supple.track' local my_objects_by_obj = {} local my_objects_by_tag = {} +local my_objects_expn_by_tag = {} local my_name = "UNSET" local my_counter = 0 local their_objects_by_tag = setmetatable({}, { __mode="v" }) local their_objects_by_obj = setmetatable({}, { __mode="k" }) +local their_objects_refcnt = {} local proc_call = nil local type = capi.rawtype local function clean_down(call_other_end) + track.enter("clean_down") -- And force a full GC gc "collect" gc "collect" @@ -35,6 +39,8 @@ local function clean_down(call_other_end) -- And forget all our local objects my_objects_by_obj = {} my_objects_by_tag = {} + my_objects_expn_by_tag = {} + track.leave("clean_down") end local function set_name(newname) @@ -76,21 +82,26 @@ local function give(obj, special_tag) end -- If the passed object is one of their objects then "unwrap" it by -- returning their tag. + track.enter("give", tostring(obj), tostring(special_tag)) local tag = their_objects_by_obj[obj]; if tag then + track.leave("give", "theirs", tag) return { tag = tag } end -- If it's one of our objects which has already been wrapped, return our -- tag local tag = my_objects_by_obj[obj] if tag then - return { tag = tag } + track.leave("give", "ours", tag) + return my_objects_expn_by_tag[tag] end -- otherwise wrap it freshly for us and return that. local tag = new_tag(special_tag) local expn = capi.explain(obj, tag) my_objects_by_obj[obj] = tag my_objects_by_tag[tag] = obj + my_objects_expn_by_tag[tag] = expn + track.leave("give", "ours, new", tag) return expn end @@ -103,25 +114,28 @@ local function receive(obj) assert(type(obj) == "table") assert(type(obj.tag) == "string") local tag = obj.tag + track.enter("receive", tag) -- First up, is it one of our objects? local ret = my_objects_by_tag[tag] if ret then + track.leave("receive", "ours", tostring(ret)) return ret end -- Now is it a known one of their objects? ret = their_objects_by_tag[tag] if ret then + track.leave("receive", "theirs", tostring(ret)) return ret end -- Okay, prepare a proxy? - assert(type(obj.type) == "string") + assert(type(obj.type) == "string", "Type string expected, got " .. type(obj.type) .. " preparing proxy for " .. obj.tag) local proxy, mt = capi.new_proxy(obj.type) assert(capi.type(proxy) == obj.type) their_objects_by_tag[tag] = proxy their_objects_by_obj[proxy] = tag + their_objects_refcnt[tag] = (their_objects_refcnt[tag] or 0) + 1 -- Fill out the metatable obj.methods = obj.methods or {} - obj.methods[#obj.methods+1] = "__gc" if obj.type == "function" then obj.methods[#obj.methods+1] = "__call" end @@ -133,18 +147,33 @@ local function receive(obj) end for _, name in ipairs(obj.methods or {}) do local function meta_func(mobj, ...) - return proc_call(tag, name, ...) + track.enter("meta_func", tostring(mobj), tag, name) + local ret = {proc_call(tag, name, ...)} + track.leave("meta_func", tostring(mobj), tag, name) + return unpack(ret) end mt[name] = meta_func end + function mt:__gc() + their_objects_refcnt[tag] = their_objects_refcnt[tag] - 1 + if their_objects_refcnt[tag] == 0 then + their_objects_refcnt[tag] = nil + proc_call(tag, "__gc") + end + end -- And return the proxy object + track.leave("receive", "theirs, new", tostring(proxy)) return proxy end local function forget_mine(tag) + track.enter("forget_mine", tag) local obj = my_objects_by_tag[tag] + assert(obj, "Trying to forget nil object: " .. tag) my_objects_by_tag[tag] = nil my_objects_by_obj[obj] = nil + my_objects_expn_by_tag[tag] = nil + track.leave("forget_mine", tag) end local function find_tag(obj) @@ -156,6 +185,17 @@ local function find_tag(obj) end end +local function get_object_anchor() + local ret = {} + for obj in pairs(their_objects_by_obj) do + ret[obj] = true + end + for obj in pairs(my_objects_by_obj) do + ret[obj] = true + end + return ret +end + return { set_name = set_name, get_name = get_name, @@ -165,4 +205,5 @@ return { clean_down = clean_down, forget_mine = forget_mine, find_tag = find_tag, + get_object_anchor = get_object_anchor, } diff --git a/lib/supple/sandbox.lua b/lib/supple/sandbox.lua index cc1bc6c..f4025bc 100644 --- a/lib/supple/sandbox.lua +++ b/lib/supple/sandbox.lua @@ -24,12 +24,15 @@ local objects = require 'supple.objects' local comms = require 'supple.comms' local luxio = require 'luxio' local sio = require 'luxio.simple' +local track = require 'supple.track' local loadstring = loadstring local load = load local setfenv = setfenv local gc = collectgarbage local unpack = unpack +local xpcall = xpcall +local traceback = debug.traceback local function set_limits(ltab) local count = ltab.count @@ -152,6 +155,7 @@ local function run() -- Pretend we've "given" the host an object called 'supple:loadstring' -- which is the loadstring/load function + track.start() objects.give(set_limits, "supple:set_limits") objects.give(wrappered_load, "supple:loadstring") objects.give(objects.clean_down, "supple:clean_down") @@ -160,6 +164,14 @@ local function run() return fn(comms._wait) end +local function wrapper_run() + local ok, retcode = xpcall(run, traceback) + if not ok then + error(retcode) + end + return retcode +end + return { - run = run, + run = wrapper_run, } diff --git a/lib/supple/track.lua b/lib/supple/track.lua new file mode 100644 index 0000000..b22156f --- /dev/null +++ b/lib/supple/track.lua @@ -0,0 +1,62 @@ +-- lib/supple/track.lua +-- +-- Sandbox (for) Untrusted Procedure Partitioning (in) Lua Engine +-- +-- Tracking and logging for debug porpoises. +-- +-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org> +-- +-- For licence terms, see COPYING +-- + +local depth +local track + +local function record(...) + if depth then + track[#track+1] = { depth, ... } + end +end + +local function enter(...) + if depth then + record(">>>", ...) + depth = depth + 1 + end +end + +local function leave(...) + if depth then + if depth > 0 then + depth = depth - 1 + end + record("<<<", ...) + end +end + +local function start_tracking() + depth, track = 0, {} +end + +local function stop_tracking() + if track == nil then + return "***NO TRACKING DATA***" + end + local ret = {} + for i = 1, #track do + local ent = track[i] + local pfx = (" "):rep(ent[1]) + table.remove(ent, 1) + ret[#ret+1] = pfx .. table.concat(ent, " ") + end + depth, track = nil, {} + return table.concat(ret, "\n") +end + +return { + start = start_tracking, + stop = stop_tracking, + enter = enter, + leave = leave, + record = record, +}
\ No newline at end of file |