使用LuaJIT编写脚本并有选择地沙盒化FFI

时间:2013-08-22 09:45:53

标签: security lua sandbox ffi luajit

在尝试并见证了我可以将Lua和LuaJIT集成到我的游戏引擎中的难以置信的轻松之后,我确信这是我想要使用的脚本语言。我想将它用于我的AI,单位描述,地图触发器等等。 (尽可能真的)。这个问题不仅适用于gamedev,我可以想象创建一个可编写脚本的编辑器或窗口管理器,它可以加载外部脚本(例如:使用python和包控制的sublime文本)

但现在我有一个难题:我非常感谢 LuaJIT FFI 提供绑定到我的引擎的易用性,但我不想提供免费的统治例如,用于映射作者的FFI 。通过FFI进行lua-to-c调用的惊人速度(当JITted时)也是我真正想要的。

理想情况下,我会编写自己的包装器Lua文件,使用FFI绑定到我的引擎,并导出一个很好的模块供地图作者和模拟器使用。我的替代方案是编写一个vanilla lua模块,这可能但更麻烦和更慢。

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

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

  1. open lua state
  2. 加载所有模块(luaL_openlibs()),FFI也预加载
  3. 加载我的.lua模块,使用FFI(这是引擎绑定,它们是受信任的文件,因此可以使用FFI)
  4. 禁用选择本机模块和功能:os,ffi,...(这是缺少的步骤)
  5. 执行用户提供的脚本(这些脚本不受信任,我不希望他们访问FFI)
  6. 可选:寻找一种方法来重新加载lua模块以实现快速编辑周期,这将涉及重新启用FFI和其他模块。 (不确定如何执行此操作
  7. 注意:我知道这仍然不是一个完美的(甚至是好的沙盒),正如Mike Pall已经在他的一些邮件中指出的那样,但我仍然不想要让地图作者访问FFI。

1 个答案:

答案 0 :(得分:4)

也许我不明白这个问题,但如果你使用无法访问FFI的a normal Lua sandbox,会出现什么问题?

例如:

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泄漏到沙箱中。有a paragraph about this in the LuaJIT documentation