/usr/share/lua/5.1/supple/comms.lua is in lua-supple 1.0.8-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | -- lib/supple/comms.lua
--
-- Sandbox (for) Untrusted Procedure Partitioning (in) Lua Engine
--
-- Management of communications between host and sandbox
--
-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
--
-- For licence terms, see COPYING
--
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
local error = error
local getinfo = debug.getinfo
local concat = table.concat
local xpcall = xpcall
local gc = collectgarbage
local fd = -1
local function set_fd(_fd)
fd = _fd
end
local function send_msg(msg)
if (#msg > 99999) then
error("Message too long")
end
local msglen = ("%05d"):format(#msg)
track.record("XMIT", msg)
luxio.write(fd, msglen .. msg)
end
local function recv_msg()
local len, errno = luxio.read(fd, 5)
if type(len) ~= "string" then
error(luxio.strerror(errno))
end
if #len < 5 then
error("Unable to read 5 byte length")
end
len = tonumber(len)
if len == nil or len < 1 or len > 99999 then
error("Odd, len didn't translate properly")
end
local str = luxio.read(fd, len)
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
local function captcha(msg)
local traceback = {}
local level = 2
local info = getinfo(level, "Snlf")
local function in_supple()
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 true then
local ttype, tag = objects.find_tag(info.func)
if ttype then
info.name = tag
end
local line =
("\t%s:%d in function %s"):format(info.short_src,
info.currentline,
info.name or "<anon>")
traceback[#traceback+1] = line
end
level = level + 1
info = getinfo(level, "Snlf")
end
return {msg, (concat(traceback, "\n") or "") .. "\nComms track:\n" .. track.stop()}
end
local limits = {}
math.randomseed(os.time())
math.randomseed(math.random(luxio.getpid()))
local count_per_hook = 10 + (math.random(5) - 3)
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
-- 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
local msg = back.message
if back.traceback and back.traceback ~= "" then
msg = msg .. "\n" .. back.traceback
end
error(msg, 2)
end
-- A result, return it
if back.error == false then
return unpack(back.results)
end
-- A method call, call it
local function safe_method(fn)
local ok, res = xpcall(fn, captcha)
local resp
if not ok then
resp = request.error(unpack(res))
else
resp = request.response(unpack(res))
end
-- Force anything no longer anchored to be GC'd before we reply
gc "collect"
send_msg(resp)
end
if back.method == "__gc" then
-- __gc is the garbage collect mechanism
objects.forget_mine(back.object)
-- Force anything no longer anchored to be GC'd before we reply
gc "collect"
send_msg(request.response())
elseif back.method == "__call" then
-- __call is the function call mechanism
safe_method(function()
local obj = objects.receive { tag = back.object }
return {obj(unpack(back.args))}
end)
elseif back.method == "__len" then
safe_method(function()
local obj = objects.receive { tag = back.object }
return {#obj}
end)
elseif back.method == "__index" then
safe_method(function()
local obj = objects.receive { tag = back.object }
return {obj[back.args[1]]}
end)
elseif back.method == "__newindex" then
safe_method(function()
local obj = objects.receive { tag = back.object }
obj[back.args[1]] = back.args[2]
return {}
end)
elseif back.method == "__next" then
safe_method(function()
local obj = objects.receive { tag = back.object }
return {next(obj, back.args[1])}
end)
else
safe_method(function()
local obj = objects.receive { tag = back.object }
local meth = capi.raw_getmm(obj, back.method)
if not meth then
error("Unknown or disallowed method: " .. back.method, "")
end
return {meth(obj, unpack(back.args))}
end)
end
until false
end
local function make_call(object, method, ...)
gc "stop"
track.enter("make_call", object, method)
local req = request.request(object, method, ...)
send_msg(req)
local ret = {wait_for_response()}
track.leave("make_call", object, method)
gc "restart"
return unpack(ret)
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,
}
|