feat(sandbox) explicitly drop support of quotas on LuaJIT

The solution we use in PUC Rio Lua (with debug.sethook) simply does not
work in LuaJIT.

* We have added a `sandbox.quota_supported` field to signal this feature
  (or lack of thereof)
* We explicitly return an error if `options.quota` is passed on a LuaJIT
  environment, in order to prevent LuaJIT users from believing that they
  are protected against infinite loops.
This commit is contained in:
Enrique García Cota 2021-01-05 00:43:04 +01:00
parent 242a749c4d
commit 71223d4fe9
No known key found for this signature in database
GPG Key ID: 3BAA19133AAD7764
3 changed files with 34 additions and 21 deletions

View File

@ -65,7 +65,7 @@ sandbox.run('while true do end') -- raise errors after 500000 instructions
sandbox.run('while true do end', {quota=10000}) -- raise error after 10000 instructions sandbox.run('while true do end', {quota=10000}) -- raise error after 10000 instructions
``` ```
Note that if the quota is low enough, sandboxed functions that do lots of calculations might fail: If the quota is low enough, sandboxed functions that do lots of calculations might fail:
``` lua ``` lua
local f = function() local f = function()
@ -77,6 +77,8 @@ end
sandbox.run(f, {quota=100}) -- raises error before the function ends sandbox.run(f, {quota=100}) -- raises error before the function ends
``` ```
Note: This feature is not available in LuaJIT
### options.env ### options.env
Use the `env` option to inject additional variables to the environment in which the sandboxed function is executed. Use the `env` option to inject additional variables to the environment in which the sandboxed function is executed.

View File

@ -25,9 +25,13 @@ local sandbox = {
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]] ]],
} }
-- quotas don't work in LuaJIT since debug.sethook works differently there
local quota_supported = type(_G.jit) == "nil"
sandbox.quota_supported = quota_supported
-- PUC-Rio Lua 5.1 does not support deactivation of binary code -- PUC-Rio Lua 5.1 does not support deactivation of binary code
local mode_supported = _ENV or type(_G.jit) == "table" local mode_supported = _ENV or type(_G.jit) == "table"
@ -125,6 +129,9 @@ function sandbox.protect(code, options)
options = options or {} options = options or {}
local quota = false local quota = false
if options.quota and not quota_supported then
error("options.quota is not supported on this environment (usually LuaJIT). Please unset options.quota")
end
if options.quota ~= false then if options.quota ~= false then
quota = options.quota or 500000 quota = options.quota or 500000
end end
@ -148,7 +155,7 @@ function sandbox.protect(code, options)
return function(...) return function(...)
if quota then if quota and quota_supported then
local timeout = function() local timeout = function()
cleanup() cleanup()
error('Quota exceeded: ' .. tostring(quota)) error('Quota exceeded: ' .. tostring(quota))

View File

@ -84,27 +84,31 @@ describe('sandbox.run', function()
end) end)
describe('when given infinite loops', function() if sandbox.quota_supported then
describe('when given infinite loops', function()
it('throws an error with infinite loops', function()
assert.error(function() sandbox.run("while true do end") end)
end)
it('throws an error with infinite loops', function() it('restores string.rep even after a while true', function()
assert.error(function() sandbox.run("while true do end") end) assert.error(function() sandbox.run("while true do end") end)
assert.equal('hellohello', string.rep('hello', 2))
end)
it('accepts a quota param', function()
assert.has_no.errors(function() sandbox.run("for i=1,100 do end") end)
assert.error(function() sandbox.run("for i=1,100 do end", {quota = 20}) end)
end)
it('does not use quotes if the quote param is false', function()
assert.has_no.errors(function() sandbox.run("for i=1,1000000 do end", {quota = false}) end)
end)
end) end)
else
it('restores string.rep even after a while true', function() it('throws an error when trying to use the quota option in an unsupported environment (LuaJIT)', function()
assert.error(function() sandbox.run("while true do end") end) assert.error(function() sandbox.run("", {quota = 20}) end)
assert.equal('hellohello', string.rep('hello', 2))
end) end)
end
it('accepts a quota param', function()
assert.has_no.errors(function() sandbox.run("for i=1,100 do end") end)
assert.error(function() sandbox.run("for i=1,100 do end", {quota = 20}) end)
end)
it('does not use quotes if the quote param is false', function()
assert.has_no.errors(function() sandbox.run("for i=1,1000000 do end", {quota = false}) end)
end)
end)
describe('when given an env option', function() describe('when given an env option', function()