2010-08-09 16 views
6

Quiero ejecutar un archivo .lua que no sea de confianza en su propio entorno llamando al lua_setfenv() para que no pueda afectar a ninguno de mis códigos.Cómo ejecutar un archivo Lua no confiable en su propio entorno desde la API C

La documentación para esa función solo explica cómo llamar a una función, no cómo ejecutarla.

la actualidad para ejecutar el archivo que utilizo:

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

¿Tengo que llamar a la "dofile" lua función de la API C con lua_setfenv, o hay una forma más elegante de hacerlo?

Respuesta

9

Consulte la discusión en la Wiki del usuario de Lua en sandboxing, y el tema más general de script security. Hay una serie de problemas sutiles y no tan sutiles con este tipo de cosas. Se puede hacer, pero la protección contra código como for i=1,1e39 do end requiere algo más que restringir qué funciones están disponibles para un sandbox.

La técnica general es crear un entorno de función para la caja de arena que tiene una lista blanca de funciones permitidas. En algunos casos, esa lista puede estar vacía, pero dejar que el usuario tenga acceso al pairs(), por ejemplo, es casi seguro que no es dañino. La página de la zona de pruebas tiene una lista de las funciones del sistema desglosadas por su seguridad como una referencia práctica para construir una lista blanca de este tipo.

Luego usa lua_setfenv() para aplicar el entorno de función al script del usuario que cargó (pero aún no ha ejecutado) con lua_loadfile() o lua_loadstring() según corresponda. Con el entorno adjunto, puede ejecutarlo con lua_pcall() y amigos. Antes de la ejecución, algunas personas han escaneado el bytecode cargado para operaciones que no desean permitir. Eso se puede usar para prohibir absolutamente los bucles o escribir en variables globales.

Otra nota es que las funciones de carga generalmente cargarán bytecode precompilado o texto Lua. Resulta ser mucho más seguro si nunca permite el bytecode precompilado, ya que se han identificado varias formas de hacer que la VM se porte mal y todas dependen de la elaboración manual de bytecode inválido. Como los archivos de código de bytes comienzan con una secuencia de bytes bien definida que no es texto ASCII simple, todo lo que necesita hacer es leer el script en un búfer de cadenas, probar la ausencia del marcador y solo pasarlo al lua_loadstring() si no lo está bytecode.

Ha habido una buena cantidad de discusión en el Lua-L mailing list durante los años de este tipo de cosas, por lo que la búsqueda allí también puede ser útil.

+0

Gracias; Ya me ocupé de los bucles infinitos configurando un enlace de depuración que finaliza el script después de 10 millones de instrucciones. En cuanto a las funciones, el archivo proporcionado por el usuario contiene solo variables globales y ninguna función, por lo que no tengo que incluir ninguna función en la lista blanca. –

2

luaL_loadfile() cargará el fragmento, luego llame al lua_setfenv() para establecer la tabla de entorno, luego llame al lua_pcall() para ejecutar el fragmento. Vea la respuesta reciente dada por el juez Maygarden al Calling lua functions from .lua's using handles?

5

Por cierto, esto es lo que terminé haciendo:

/* 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; 
} 

"apoyo" funciones:

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); } 
Cuestiones relacionadas