load, Chunks, Bytecode and Sandboxing
-- load compiles a string (or reader function) into a function
local f = load("return 2 + 3")
print(f()) --> 5
local add = load("local a, b = ...; return a + b")
print(add(4, 5)) --> 9
local fn, err = load("return +") -- syntax error
print(fn, err) --> nil [string "return +"]:1: unexpected symbol near <eof>
-- Restrict mode and environment
local env = {math = math}
local calc = load("return math.sqrt(x)", "calc", "t", setmetatable(env, {__index = {x = 16}}))
print(calc()) --> 4.0
dofile("script.lua") -- load and run (errors propagate)
local chunk = loadfile("script.lua", "t") -- compile only
-- Bytecode
local bytecode = string.dump(function(a) return a * 2 end)
local restored = load(bytecode, "dumped", "b") -- mode "b" = binary only
print(restored(21)) --> 42
-- strip debug info: string.dump(f, true)30.1 Building a Safe Sandbox
Never load untrusted bytecode. Exclude load, loadstring, dofile, require, io, os, debug, setmetatable/getmetatable (string metatable access), collectgarbage, and rawset from sandboxes. Memory limits require a custom allocator (C API: lua_setallocf).
local safe_env = {
print = print, pairs = pairs, ipairs = ipairs, next = next, type = type,
tostring = tostring, tonumber = tonumber, select = select, error = error, pcall = pcall,
string = {format = string.format, sub = string.sub, upper = string.upper, lower = string.lower,
len = string.len, find = string.find, gsub = string.gsub, match = string.match, gmatch = string.gmatch},
table = {insert = table.insert, remove = table.remove, sort = table.sort, concat = table.concat, unpack = table.unpack},
math = math,
}
local function run_sandboxed(code, max_instructions)
local fn, err = load(code, "user", "t", safe_env) -- "t": refuse bytecode (malicious bytecode can crash the VM)
if not fn then return nil, err end
local co = coroutine.create(fn)
debug.sethook(co, function() error("instruction limit exceeded") end, "", max_instructions or 1e6)
local ok, result = coroutine.resume(co)
return ok and result or nil, (not ok) and result or nil
end
print(run_sandboxed("return 1 + 1"))
print(run_sandboxed("while true do end", 1e5))
print(run_sandboxed("os.exit()"))