lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Luke Mewburn <luke@mewburn.net> writes:

> The original ideas for the sandbox were derived from my reading
> of the various Lua literature available a few years ago when Lua 5.1
> was the current release.
>
> I'm seeking advice on the best way to use Lua 5.2 from C/C++
> to load chunks in a sandboxed environment and subsequently invoke
> lua functions from C/C++ in that same sandbox.
>
> With the change in Lua 5.2 to deprecate lua_setfenv() in favour of other
> techniques (upvalues ?), I would like to update the C++ wrapper library
> for Lua 5.2/5.3 as well as continuing to support Lua 5.1.
>
> It may be that the pattern I used for Lua 5.1 can't be used for Lua 5.2
> and I need to redesign my wrapper library. I'd prefer to do that in a
> way that reduces change on callers of the library.
>
> Current use in Lua 5.1:
>
> The sandboxing setup technique used in Lua 5.1 is:
> 	a) create a table for the environment
> 	b) copy names of "safe" globals into the table, such as
> 	   assert(), ipairs(), string.*, (etc)
> 	c) add other (private) extensions to the table
> 	d) add the table to the global environment with a well-known name
>
> To invoke a chunk or function 
> 	a) limit instructions / maskcount
> 	b) get the sandbox table from the global environment
> 	c) set the environment for the chunk/function with lua_setfenv()
> 	d) call the chunk/function

So, typically in 5.2, it makes more sense to:
        a) create the sandbox table
        b) load the user code as chunks
        c) set the first upvalue of each chunk to the sandbox table

Then user functions are just used normally.  Their globals will always
be from the sandbox table.

[...]

> Attempt to convert to Lua 5.2:
>
> I've tried converting our use of lua_setfenv() to lua_setupvalue() but
> it's not setting up the environment correctly when trying to set
> the environment for a function call (versus a chunk call).
> lua_setupvalue(L, -3, 1) is returning NULL when the stack has;
> 	1: function
> 	2: table as function argument
> 	3: table to use as environment
>
> I.e. When I change Lua 5.1's
> 	lua_setfenv(L, -iNumArgs - 2))
> to Lua 5.2's
> 	lua_setupvalue(L, -iNumArgs - 2, 1)
> my sandboxing doesn't work.

Right.  Only chunks have this implicit _ENV upvalue.  Functions within
the chunk get this upvalue from their containing chunk.  I suppose it's
possible to get to it from the function, but that honestly seems like a
pain to accomplish.

> Pseudo-code of the Lua 5.1 sandbox technique:

[...]

> // Call function on stack in a sandbox (maskcount, restricted environment)
> sandboxCall(lua_state * L, int iNumArgs, int iNumResults)
> 	lua_sethook(L, &maskcount, LUA_MASKCOUNT, someLimit);
> 	lua_getglobal(L, "LuaSandBox");		// set environment to sandbox
> 	lua_setfenv(L, -iNumArgs - 2);
> 	lua_pcall(L, iNumArgs, iNumResults);
> 	lua_sethook(L, &maskcount, LUA_MASKCOUNT, 0);
>
> // Setup table for use as a sandbox environment. Caller may extend.
> setupSandbox(lua_State * L)
> 	// copy values from global environment into sandbox table on stack top
>
> // Call a lua function from C that takes a table argument & returns a bool
> fooFunction(lua_State * L)
> 	findName(L, "some_method");
> 	if (! lua_isfunction(L, -1)) error();
> 	lua_createTable(L, 0, 1);
> 	lua_pushstring(L, "some value");
> 	lua_setfield(L, "k");		// { k = "somevalue", }
> 	sandboxCall(L, 1, 1)		// invoke some_method({k="somevalue"})
> 	bool result = lua_toboolean(L, -1)
> 	lua_pop(L, 1);
> 	return result;

If you really want to change as little as possible, then something like
the below is an option.  During setup it creates a chunk that just calls
the passed in function and arguments.  To call a function within an
environment, it pushes the apply chunk, sets it's _ENV upvalue, and then
calls it with the function and its arguments.  (Code written in my MUA,
and as such completely untested!  May contain indexing errors.  But
should be sufficient to explain a possible solution.)

const char *apply_code = "local fn = ...; return fn(select(2,...))";
static int apply_ref;
static void setup(lua_State *L) {
    luaL_loadstring(L, apply_code);
    call_with_env_ref = luaL_ref(L, LUA_REGISTRYINDEX);
}

int sandboxpcall(lua_State *L, int envtable, int nargs, int nresults) {
    int rv;
    int fn_index = lua_absindex(L, -nargs - 1);
    envtable = lua_absindex(L, envtable);
    lua_rawgeti(L, LUA_REGISTRYINDEX, apply);
    lua_pushvalue(L, envtable);
    lua_setupvalue(L, -2, 1);
    lua_insert(L, fn_index);
    return lua_pcall(L, nargs + 1, nresults, 0);
}

-- 
Michael Welsh Duggan
(mwd@cert.org)