Как выполнить ненадежный файл Lua в своей среде из C API - PullRequest
6 голосов
/ 10 августа 2010

Я хочу выполнить ненадежный файл .lua в своей среде, вызвав lua_setfenv () , чтобы он не мог повлиять ни на один мой код.

В документации по этой функции объясняется только то, как вызывать функцию, а не как выполнять файл.

В настоящее время для запуска файла я использую:

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

Нужно ли вызывать функцию lua "dofile" из C API с помощью lua_setfenv, или есть более элегантный способ сделать это?

Ответы [ 3 ]

9 голосов
/ 10 августа 2010

См. Обсуждение на вики пользователя Lua песочницы и более общую тему безопасности сценариев . Есть много тонких и не очень тонких проблем с такими вещами. Это можно сделать, но защита от кода, такого как for i=1,1e39 do end, требует больше, чем просто ограничение того, какие функции доступны в песочнице.

Общий метод заключается в создании функциональной среды для песочницы, в которой есть белый список разрешенных функций. В некоторых случаях этот список может быть даже пустым, но, например, предоставление пользователю доступа к pairs() почти наверняка безвредно. Страница с песочницей содержит список системных функций, разбитых по их безопасности, как удобный справочник для создания такого белого списка.

Затем вы используете lua_setfenv(), чтобы применить функциональную среду к пользовательскому сценарию, который вы загрузили (но еще не выполнили) с помощью lua_loadfile() или lua_loadstring(), в зависимости от ситуации. С подключенной средой вы можете выполнить ее с lua_pcall() и друзьями. Перед выполнением некоторые люди на самом деле сканировали загруженный байт-код на наличие операций, которые они не хотят разрешать. Это может быть использовано для полного запрета циклов или записи в глобальные переменные.

Еще одно замечание: функции загрузки обычно загружают либо предварительно скомпилированный байт-код, либо текст Lua. Оказывается, намного безопаснее, если вы никогда не разрешите предварительно скомпилированный байт-код, так как было выявлено несколько способов неправильного поведения виртуальной машины, все они зависят от ручного создания недействительного байт-кода. Поскольку файлы байт-кода начинаются с четко определенной последовательности байтов, которая не является простым текстом ASCII, все, что вам нужно сделать, это прочитать скрипт в строковый буфер, проверить отсутствие маркера и передать его только lua_loadstring(), если не является байт-кодом.

В течение многих лет такого рода рассылок Lua-L велось немало дискуссий, так что поиск там также может быть полезным.

5 голосов
/ 09 ноября 2010

Кстати, вот что я в итоге сделал:

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

Функции «Поддержка»:

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); }
2 голосов
/ 10 августа 2010

luaL_loadfile() загрузит чанк, затем вызовет lua_setfenv(), чтобы установить таблицу окружения, затем вызовет lua_pcall(), чтобы выполнить чанк.Смотрите недавний ответ, данный судьей Мейгарден на Вызов функций lua из .lua с помощью ручек?

...