summaryrefslogtreecommitdiff
path: root/lib/supple/host.lua
blob: d78211516437795b13527179bfe54c0a2a32a78d (plain)
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
-- 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
--

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 = "@@WRAPPER_BIN@@"
   -- START_TEST_SUPPLE
   wrapperpath = os.getenv "SUPPLE_TEST_WRAPPER"
   -- 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)

   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 .. "\n\nHost comms track:\n" .. track.stop()
   end
end

local function sandboxed_loadstring(codestr, codename)
   return comms.call("supple:loadstring", "__call", codestr, codename)
end

local function set_hostname(newname)
   hostname = newname
   counter = 0
end

local function set_limits(newlimits)
   limits = newlimits
end

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,
}