How to execute untrusted Lua file in your environment from C API

I want to execute an untrusted .lua file in my own environment by calling lua_setfenv () so that it cannot affect any of my codes.

The documentation for this function explains how to call the function, not how to execute the file.

Currently, to run the file, I use:

int error = luaL_loadfile(mState, path.c_str()) || lua_pcall(mState, 0, 0, 0); 

Should I call the "dofile" lua function from the C API using lua_setfenv , or is there a more elegant way to do this?

+6
c ++ c lua lua-api
source share
3 answers

See the discussion in the Lua User Wiki sandbox , and the more general topic of script security . There are a number of subtle and not so subtle problems with these kinds of things. This can be done, but protection from code such as for i=1,1e39 do end requires more than just limiting which features are available for the sandbox.

The general methodology is to create a functional environment for the sandbox, in which there is a white list of allowed functions. In some cases, this list may even be empty, but, for example, the user can have access to pairs() , for example, is practically harmless. On the sandbox page there is a list of system functions, broken by their security, as a convenient link to build such a white list.

Then you use lua_setfenv() to apply the function environment to the user script that you loaded (but have not yet executed) with lua_loadfile() or lua_loadstring() , if necessary. With a connected environment, you can execute it using lua_pcall() and friends. Before executing, some people actually scanned the downloaded bytecode for operations that they did not want to allow. This can be used to completely inhibit loops or write global variables.

Another note is that load functions usually load either precompiled bytecode or Lua text. It turns out that it is much safer if you never allow a pre-compiled bytecode, since several ways have been revealed to make the virtual machine behave erroneously, which all depends on manual work with invalid bytecode. Since bytecode files start with a well-defined byte sequence, which is not plain ASCII text, all you have to do is read the script into a string buffer, check for the absence of a marker and pass it only to lua_loadstring() if it is not bytecode .

For many years, this kind of discussion was held in the Lua-L mailing list section, so searching there is also likely to be useful.

+9
source share

By the way, this is what I ended up with:

 /* Loads, compiles and executes an unstrusted file. */ bool Lua::RunUntrustedFile(const string& path) { if(luaL_loadfile(mState, path.c_str())) { ErrorLog(lua_tostring(mState, 1)); Pop(1); return false; } Lua::SetMaximumInstructions(100000000); lua_newtable(mState); lua_setglobal(mState, "upload"); ASSERT(Lua::GetStackSize() == 1); lua_getglobal(mState, "upload"); ASSERT_ALWAYS(lua_setfenv(mState, 1) != 0); ASSERT(Lua::GetStackSize() == 1); if(lua_pcall(mState, 0, 0, 0)) { Lua::ClearMaximumInstructions(); ErrorLog(lua_tostring(mState, -1)); Pop(1); return false; } ASSERT(Lua::GetStackSize() == 0); Lua::ClearMaximumInstructions(); return true; } 

Support Functions:

 static void Pop(int elements = 1) { lua_pop(mState, elements); } /* Sets a maximum number of instructions before throwing an error */ static void SetMaximumInstructions(int count) { lua_sethook(mState, &Lua::MaximumInstructionsReached, LUA_MASKCOUNT, count); } static void ClearMaximumInstructions() { lua_sethook(mState, &Lua::MaximumInstructionsReached, 0, 0); } static void MaximumInstructionsReached(lua_State *, lua_Debug *) { Error("The maximum number of instructions has been reached"); } static int GetStackSize() { return lua_gettop(mState); } 
+5
source share

luaL_loadfile() load the piece, then call lua_setfenv() to set the environment table, then call lua_pcall() to execute the piece. See Judge Maygardenโ€™s recent answer in Calling lua Functions from .lua Handles?

+2
source share

All Articles