Search code examples
c++clualua-api

Execution time limit for a Lua script called from the C API


luaL_loadfile(mState, path.c_str());
lua_pcall(mState, 0, 0, 0);

Is there a way to put an execution time limit (say 10-20 seconds) for those two C++ statements, that load and then execute a lua file?

Since the Lua file is untrusted I don't want a malicious user to hang the program indefinitely with an infinite loop in the Lua code.

Tagging C because the Lua API is C, tagging C++ because I'm using C++


Solution

  • There's lua_sethook which can be used to tell the interpreter to call a hook after every 'count' instructions executed. This way you can monitor the user script and terminate it if it eats up its quota:

     int lua_sethook (lua_State *L, lua_Hook f, int mask, int count);
    

    This can also be used from Lua:

    debug.sethook(function() print("That's enough for today"); os.exit(0); end, "", 10000)
    for i=1,10000 do end
    

    If you use the techniques from http://lua-users.org/wiki/SandBoxes then you can set up a safe execution environment with sethook() and friends entirely from Lua and then switch to sandbox mode while executing the user script. I've tried that here, just for you to get started:

    -- set an execution quota 
    local function set_quota(secs)
     local st=os.clock()
     function check() 
      if os.clock()-st > secs then 
        debug.sethook() -- disable hooks
        error("quota exceeded")
      end
     end
     debug.sethook(check,"",100000);
    end
    
    -- these are the global objects, the user can use:
    local env = {print=print}
    
    -- The user code is allowed to run for 5 seconds.
    set_quota(5)
    
    -- run code under environment:
    local function run(untrusted_code)
      local untrusted_function, message = loadstring(untrusted_code)
      if not untrusted_function then return nil, message end
      setfenv(untrusted_function, env)
      return pcall(untrusted_function)
    end
    
    -- here is the user code:
    local userscript=[[
    function fib(n) 
     if n<2 then return n
     else return fib(n-2)+fib(n-1)
     end
    end
    for n=1,42 do print(n,fib(n)) end
    ]]
    -- call it:
    local r,m=run(userscript)
    print(r,m)
    

    This should print values of fib() for 5 seconds and then show an error.