使用LuaJIT进行脚本编写并选择性地对FFI进行沙盒保护。

在尝试并亲身见证了我能够轻松地将Lua和LuaJIT集成到我的游戏引擎中之后,我相信这是我想使用的脚本语言。我想将其用于我的AI、单位描述、地图触发器等(尽可能多的使用它)。这个问题不仅适用于游戏开发,我可以想象创建一个可脚本化的编辑器或窗口管理器,可以加载外部脚本(例如:基于Python和Package Control的Sublime Text)。

但现在我有一个难题:我真的很欣赏LuaJIT FFI所提供的易用性,可以将其与我的引擎绑定起来,但是我不想为地图作者提供FFI的自由使用权。当JIT化时,通过FFI进行的lua-to-c调用的惊人速度也是我真正想要的东西。

因此,理想情况是,我将编写自己的包装器Lua文件,用FFI将其绑定到我的引擎上,并导出一个漂亮的模块供地图作者和模块制作者使用。我替代方案是编写一个vanilla lua模块,这是可能的,但更加繁琐和缓慢。

我不能在编译luajit时禁用FFI,因为显然我自己想使用它,但我不知道如何在每个脚本或每个模块的基础上限制FFI。显然,FFI需要在我加载模块的lua_State中处于活动状态(在此之后,我无法开始加载用户修改的脚本)。那我该怎么办?这可能吗?

编辑:在我看来,理想的工作流应该是:

  1. 打开lua状态
  2. 加载所有模块(luaL_openlibs()),FFI也预先加载
  3. 加载我的.lua模块,它们使用FFI(这是引擎绑定,它们是可信的文件,因此它们可以使用FFI)
  4. 禁用选择的本地模块和函数:os,ffi,…(这是缺失的步骤)
  5. 执行用户提供的脚本(这些是不受信任的,我不想让它们访问FFI)
  6. 可选:寻找重新加载lua模块的方法,以进行快速的编辑循环,这将涉及重新启用FFI和其他模块。(我也无法确定如何做到这一点)。

注意:我知道这仍然不是完美的(甚至不是好的)沙箱,正如Mike Pall在他的某些邮件中指出的那样,但我仍然不想给地图作者访问FFI的权限。

点赞
用户204011
用户204011

也许我没有理解这个问题,但如果你在一个 普通的 Lua 沙盒 中使用 FFI 无法访问,那么问题在哪里?

例如:

ffi = require "ffi"

ffi.cdef([[int printf(const char *fmt, ...);]])

function say_hello()
  ffi.C.printf("%s", "hello, ");
end

my_user_script = [[
say_hello()
ffi.C.printf("%s\n", "world")
]]

f = loadstring(my_user_script)

print("=== without sandbox ===")
print(pcall(f))

print("=== with sandbox ===")
setfenv(f,{say_hello = say_hello})
print(pcall(f))

这应该会输出:

=== without sandbox ===
hello, world
true
=== with sandbox ===
hello, false    [string "say_hello()..."]:2: attempt to index global 'ffi' (a nil value)

请注意,您还需要小心不要将 FFI cdata 泄露到沙盒中。在 LuaJIT 文档中有 一个关于此的段落

2013-08-22 10:06:54