/usr/share/lua/5.1/supple/host.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 232 233 234 235 236 237 238 239 240 241 242 | -- lib/supple/host.lua
--
-- Sandbox (for) Untrusted Procedure Partitioning (in) Lua Engine
--
-- Management of the host side of Supple
--
-- Copyright 2012 Daniel Silverstone <dsilvers@digital-scurf.org>
--
-- For licence terms, see COPYING
--
--- Running the host-side of sandboxed code
--
-- When running code in Supple sandboxes, the starting point is this module.
-- The host application should require supple and then begin from here...
--
-- local supple = require 'supple'
-- print(supple.host.run("return ...", "example code", "arg1", "arg2"))
-- -- Expect: 'arg1 arg2' as output
--
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 = capi.wrapper_path
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
--- Run some code in a Supple sandbox
--
-- Call this routine to run some code in a Supple sandbox. The code is
-- compiled and run entirely within the sandbox process. Any kind of value
-- can be passed to the code as an argument. Intrinsics are transferred
-- immediately (numbers, booleans, nils, strings) but complex values (tables,
-- userdata, and functions) are sent over as a proxy and actions on those
-- values are proxied back to the host to be run. Users of Supple should be
-- aware that those routines are therefore not sandboxed.
--
-- @tparam string codestr The code to run in the sandbox, as a string.
-- @tparam string codename The name to give to the code running in the sandbox.
-- @param[opt] ... Arguments to pass to the code running in the sandbox.
-- @treturn[1] boolean A value which has 'truth'
-- @return[1] ... The results returned from running the code in the sandbox.
-- @treturn[2] boolean A value which has 'falsehood'
-- @treturn[2] string The basic Lua error message associated with the failure
-- @treturn[2] string The Supple traceback (very large, not useful for users)
-- @function run
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(("%s[%d,%%d]"):format(hostname,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, track.stop()
end
end
---
-- Load a string into the sandbox and return it as a wrappered function.
--
-- This loads the given string (with given name) into the sandbox interpreter
-- and then wrappers the function and returns it to the caller. This can then
-- be used to call code inside the sandbox. This is the entry point to load
-- further code into the sandbox and should only be called during running
-- sandboxed code (i.e. `supple.host.run` is running)
--
-- @tparam string codestr The code to be loaded in the sandboxed interpreter
-- @tparam string codename The name to be given to the code when it is loaded
-- @treturn function The wrappered function which can then be called or handed
-- back to the sandbox as needed.
-- @treturn table The function environment for the loaded sandboxed code.
-- @function loadstring
local function sandboxed_loadstring(codestr, codename)
return comms.call("supple:loadstring", "__call", codestr, codename)
end
---
-- Set the name by which the host refers to itself in traces.
--
-- Calling this resets the host sandbox counter and sets the name of the host
-- to the given new name. This mostly has an effect on object names and trace
-- data which typically only appear if the user of Supple chooses to trace the
-- activity of the sandbox, or if an error occurs.
--
-- @tparam string newname The new host name to set
-- @function set_name
local function set_hostname(newname)
hostname = newname
counter = 0
end
---
-- Set a new limits table for use in a new sandbox instance.
--
-- When a sandbox is run (from `supple.host.run`) it has a number of soft
-- limits sent to it to be honoured during the runtime of the sandbox.
-- Normally the sandbox limits itself only by hard limits, but this call allows
-- tighter soft limits to be set if so-desired.
--
-- The table provided takes the form:
--
-- { [count = maxopcodestorun], [memory = maxbytestouse] }
--
-- If you call this function then at least one, but optionally both of the
-- count and memory table entries need to be present.
--
-- @tparam table newlimits The new soft-limits for any new sandboxes
-- @function set_limits
local function set_limits(newlimits)
limits = newlimits
end
---
-- Set a new globals table to be used by new sandbox runs.
--
-- When a sandboxed function is run, it is run within a given globals table.
-- The globals in question are provided by calling this function. Remember
-- that the table will be passed *shallowly* and new globals created by the
-- sandboxed code will not be copied back to the host.
--
-- @tparam table newglobals The set of globals to be passed to the sandbox
-- @function set_globals
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,
}
|