From d73be348acdf9f18479734fd09ddbfdb017c47bf Mon Sep 17 00:00:00 2001 From: Daniel Silverstone Date: Sat, 4 Aug 2012 13:05:50 +0100 Subject: SUPPLE: Basic instruction count and/or memory consumption limits for the sandbox --- lib/supple/comms.lua | 35 +++++++++++++++++++++++++++++++++++ lib/supple/host.lua | 18 +++++++++++++++++- lib/supple/sandbox.lua | 27 +++++++++++++++++---------- 3 files changed, 69 insertions(+), 11 deletions(-) (limited to 'lib') diff --git a/lib/supple/comms.lua b/lib/supple/comms.lua index d175bff..ea80fad 100644 --- a/lib/supple/comms.lua +++ b/lib/supple/comms.lua @@ -21,6 +21,7 @@ local error = error local getinfo = debug.getinfo local concat = table.concat local xpcall = xpcall +local gc = collectgarbage local fd = -1 @@ -77,6 +78,32 @@ local function captcha(msg) return {msg, concat(traceback, "\n") or ""} end +local limits = {} +local count_per_hook = 10 +local limiting = false + +local function limit_hook() + local inside = getinfo(2, "Snlf") + if inside.short_src == "[C]" or inside.name == "(tail call)" then + return + end + if limiting and not inside.short_src:match("/supple/[^%.]+%.lua$") then + if limits.count then + limits.count = limits.count - count_per_hook + if limits.count < 0 then + limiting = false + error("Used too many instructions", 2) + end + end + if limits.memory then + if limits.memory < gc "count" then + limiting = false + error("Exceeded memory usage limit", 2) + end + end + end +end + local function wait_for_response() repeat local back = request.deserialise(recv_msg()) @@ -150,8 +177,16 @@ local function make_call(object, method, ...) return wait_for_response() end +local function set_limits(newlimits) + limits = newlimits + debug.sethook() + debug.sethook(limit_hook, "c", count_per_hook) + limiting = true +end + return { call = make_call, _wait = wait_for_response, _set_fd = set_fd, + set_limits = set_limits, } \ No newline at end of file diff --git a/lib/supple/host.lua b/lib/supple/host.lua index f0181fa..bac95ae 100644 --- a/lib/supple/host.lua +++ b/lib/supple/host.lua @@ -17,6 +17,7 @@ local objects = require 'supple.objects' local hostname = "host" local counter = 0 +local limits local function run_wrapper() local wrapperpath = "@@WRAPPER_BIN@@" @@ -63,12 +64,22 @@ local function run_sandbox(codestr, codename, ...) -- 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, err = pcall(capture) + local ok + if limitsok then + ok, err = pcall(capture) + else + ok, err = limitsok, limitserr + end -- We need to clean up, so dump all the objects func = nil -- And ask the supple API to clear down too @@ -89,7 +100,12 @@ local function set_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, } \ No newline at end of file diff --git a/lib/supple/sandbox.lua b/lib/supple/sandbox.lua index 3c807cb..57e5cd3 100644 --- a/lib/supple/sandbox.lua +++ b/lib/supple/sandbox.lua @@ -28,17 +28,23 @@ local sio = require 'luxio.simple' local loadstring = loadstring local load = load local setfenv = setfenv +local gc = collectgarbage + +local function set_limits(ltab) + local count = ltab.count + local memory = ltab.memory + local limits = { count = count, memory = memory} + if limits.memory then + -- Bump memory limit by current usage to be kinder + limits.memory = limits.memory + (gc "count") + end + if not limits.count and not limits.memory then + return false, "Expected an opcode count or total memory limit" + end + comms.set_limits(limits) + return true +end --- Run fn with globs as its globals. Returns a function to run which --- returns the return values of fn, and also wrap returns the table --- which will be filled with any new globals fn creates. --- --- If fn is a string then it is used as the source of the function, if it --- is a function then it is expected to be a closure which when called --- repeatedly returns more data loaded (perhaps from a file?). src is --- expected to be the name associated with this source code. --- --- In case of error, returns nil, errmsg local function _wrap(fn, src, globs) globs = globs or {} local fn_glob = setmetatable({}, { __index = globs, __metatable=true }) @@ -127,6 +133,7 @@ local function run() -- Pretend we've "given" the host an object called 'supple:loadstring' -- which is the loadstring/load function + objects.give(set_limits, "supple:set_limits") objects.give(wrappered_load, "supple:loadstring") objects.give(objects.clean_down, "supple:clean_down") comms._set_fd(0) -- cgit v1.2.1