如何从C API中在Lua文件的独立环境中执行一个不可信的Lua文件。

我想通过调用 lua_setfenv() 在单独的环境中执行一个不可信的 .lua 文件,以便它不会影响我的任何代码。

不过,该功能的文档仅解释了如何调用函数,而没有解释如何执行文件。

目前,我使用以下代码运行文件:

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

我是否需要使用 lua_setfenv 从 C API 调用 "dofile" lua 函数,或者是否有更优雅的方法?

原文链接 https://stackoverflow.com/questions/3444312

点赞
stackoverflow用户173806
stackoverflow用户173806

luaL_loadfile() 函数会加载代码块,然后调用 lua_setfenv() 设置环境表,最后调用 lua_pcall() 执行代码块。请参考 Judge Maygarden 在 Calling lua functions from .lua's using handles? 中给出的最新答案。

2010-08-09 22:17:59
stackoverflow用户68204
stackoverflow用户68204

请参阅 Lua 用户 Wiki 上有关沙箱技术(sandboxing)的讨论,以及更一般的脚本安全(script security)主题。这种技术涉及到一些微妙或不太微妙的问题。它可以实现,但是保护自己不受 for i=1,1e39 do end 等代码的威胁需要不仅仅是限制沙箱可以使用哪些函数。

通常的技术是在沙箱中创建一个函数环境,其中包含了允许使用的函数白名单。在某些情况下,列表甚至可能为空,但是让用户可以访问 pairs() 这样的函数几乎肯定是无害的。沙箱页面上列出了系统函数按安全性分解的列表,可以作为构建白名单的便捷参考。

然后使用 lua_setfenv() 将函数环境应用到用户脚本上,该脚本是使用适当的 lua_loadfile()lua_loadstring() 加载的(但尚未执行)。在附加环境之前,一些人实际上已经扫描了加载的字节码,以查找不想允许的操作。这可以用来绝对禁止循环或写入全局变量。

另一个注意事项是加载函数通常会加载预编译的字节码或 Lua 文本。如果你从未允许过预编译的字节码会更加安全,因为已经发现了许多使虚拟机(VM)发生异常的方式,这些方式都依赖于手工制作的无效字节码。由于字节码文件以非普通 ASCII 文本开始,因此你只需要将脚本读入字符串缓冲区中,检测标记的短缺,并且仅在不是字节码时将其传递给 lua_loadstring()

多年来,Lua-L 邮件列表上也就此类问题进行了相当多的讨论,因此在那里搜索也可能是有帮助的。

2010-08-09 23:50:00
stackoverflow用户95135
stackoverflow用户95135

顺便说一下,这是我最终所做的:

/* 加载、编译并执行一个不受信任的文件 */
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); }

/* 设置一个最大指令数,超过此数即抛出错误 */
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("已达到最大指令数");
}

static int GetStackSize() { return lua_gettop(mState); }
2010-11-08 21:25:03