5
votes

After trying and witnessing the incredible ease with which I could integrate Lua and LuaJIT into my game engine, I'm convinced that that's the scripting language I want to use. I would like to use it for my AI, unit descriptions, map triggers, et cetera. (as much as possible really). This question is not just applicable to gamedev, I can imagine creating a scriptable editor or window manager which can load external scripts (case in point: sublime text with python and package control)

But now I have a conundrum: I really appreciate the ease of use that the LuaJIT FFI offers to bind to my engine, but I don't want to provide free reign of the FFI to map authors for example. The incredible speed of lua-to-c calls via the FFI (when JITted) is also something I really want.

So ideally, I would write my own wrapper Lua files that bind to my engine with the FFI and export a nice module for map authors and modders to use. My alternative to this is to write a vanilla lua module, which is possible but much more cumbersome and slower.

I can't disable the FFI when compiling luajit because obviously I want to use it myself, but I don't see how to restrict the FFI on a per-script or per-module basis. Obviously the FFI needs to be active in the lua_State where I load my modules (after which I cant start loading the user-modified scripts). So what do I do? Is it even possible?

EDIT: In my mind, the ideal workflow would be:

  1. open lua state
  2. load all modules (luaL_openlibs()), the FFI is also preloaded
  3. load my .lua modules, which use the FFI (this is the engine binding, they are trusted files so they can use the FFI)
  4. disable select native modules and functions: os, ffi, ... (this is the missing step)
  5. execute user-provided scripts (these are untrusted, I don't want them to access the FFI)
  6. optional: look for a way to reload lua modules for a fast edit cycle, this would involve re-enabling the FFI and other modules. (not sure how to do this either)

NOTE: I'm aware that this would still not be a perfect (or even good sandbox), as Mike Pall already pointed out in some of his mails, but I still don't want to give map authors access to the FFI.

1

1 Answers

4
votes

Maybe I do not understand the issue, but if you use a normal Lua sandbox in which the FFI is not accessible, what is the problem?

For instance:

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))

This should output:

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

Note that you also need to be careful not to leak FFI cdata into the sandbox. There is a paragraph about this in the LuaJIT documentation.