Reputation: 12797
I am trying to use How can I create a secure Lua sandbox? to build my own leaky sandbox.
I am trying to create a Lua sandbox where some Lua functions can access some other Lua functions outside the sandbox. For example I want my sandbox to have a special "display" function which can call "print" but not have "print" in the sandbox too.
The main problem is that I am trying to build a sandbox within an already large codebase, so I cannot nil away functions.
How is this possible?
The solution has to be a pure Lua function due to no fault of mine.
Upvotes: 4
Views: 1724
Reputation: 263509
Build your sandbox (or multiple sandboxes if they each have different requirements) and move the untrusted code into the sandbox one piece at a time. In my quick cli tests, both 5.1 and 5.2 will run functions that were defined outside of the sandbox without modification. To use Doug's example, assume display
is part of your pre-existing code that uses print
:
-- 5.1
local function display(...)
print(...)
end
local script = loadstring "display(math.log(2, 3))"
local env = {display = display, math = math, string = string}
setfenv(script, env)
print(pcall(script))
-- 5.2
local function display(...)
print(...)
end
local script = loadstring "display(math.log(2, 3))"
local e=_ENV
_ENV={display = display, math = math, string = string}
e.print(e.pcall(script))
_ENV=e
Note that in both of the above examples, the display
function is using print
without modification to that code since you were not in the sandbox when this function was created.
In the past, I've stored a local pointer to the un-sandboxed environment, but I can't reproduce the situation where that's needed in my quick cli tests. If you can come up with an example, I can probably come up with a workaround that doesn't require the e
variable. Here's an example of that code using 5.2:
local e=_ENV
for k,v in e.pairs(value) do
-- iterate
end
another example, for my read only table code, I'm again using the e
:
function ro_table (t)
local t = t
if t then
return e.setmetatable({},
{ __index=t,
__newindex= function(_,_,_) e.error ("Attempt to modify read-only table") end,
})
else
return nil
end
end
Upvotes: 0
Reputation: 41170
When you create a sandbox, you do it by cherry picking functions and values from a larger environment to create a new sandbox environment. You do not need to destroy or "nil out" anything in the original environment.
So,
local script = loadstring "display(math.log(2, 3))"
local env = {display = print, math = math, string = string}
setfenv(script, env)
pcall(script)
prints
0.69314718055995
whereas
local script = loadstring "print(math.log(2, 3))"
local env = {display = print, math = math, string = string}
setfenv(script, env)
pcall(script)
fails with
false [string "print(math.log(2, 3))"]:1: attempt to call global 'print' (a nil value)
Upvotes: 5
Reputation: 473272
Does it specifically need to call the Lua standard library print
function? Can you instead emulate the functionality of print
? Because that would be the easiest way.
However, if you want to have a wrapper around print
, there are two ways to do it: with pure Lua code, and with C/C++ code.
The pure Lua solution is as follows. Note that this should be done before loading any external scripts. First, open the Lua standard library that has print
in it. Then run this Lua script:
local internal_print = print
return function(...)
--Do display logic.
internal_print(...) --Or whatever else you want.
end
This will return the "display" function. You can store it in a global variable called display
if you like, or called something else.
After that, you can nil
out the print
global variable, thus making it almost entirely inaccessible.
If you want to do it from C/C++, it's very similar. First, as before, you register the Lua standard library that includes print
, so that you can get the function for it. Then, you use lua_getglobal(L, "print")
to get the print
function and push it onto the stack. Next, you register your C/C++ function using using lua_pushcclosure
. But you want to specify one upvalue, which Lua pops off the stack at registration time.
And now your registered function is on the stack, waiting to be pushed into a Lua variable or global table entry.
Warning: the Lua debug library can poke at upvalues and thus get the print
function from your new function. So if you want perfect security, get rid of debug.getupvalue
.
Upvotes: 1