Lua VM hack for execution time limits.

Posted by ThomasWatts on Tue 29 Jan 2008 06:39 AM — 3 posts, 18,994 views.

USA #0
The following assumes you can compile the Lua code yourself in the required DLL. It adds a simple time check to stop infinite loops and other malicious Lua code.
The changes should be obvious. Starts at line 374 in lvm.c

void luaV_execute (lua_State *L, int nexeccalls) {
  LClosure *cl;
  StkId base;
  TValue *k;
  const Instruction *pc;
  int origClock = (clock() / CLOCKS_PER_SEC); // <--add start
  int runtClock = 0; // <--add stop
 reentry:  /* entry point */
  lua_assert(isLua(L->ci));
  pc = L->savedpc;
  cl = &clvalue(L->ci->func)->l;
  base = L->base;
  k = cl->p->k;
  /* main loop of interpreter */
  for (;;) {
    const Instruction i = *pc++;
    StkId ra;
    runtClock = (clock() / CLOCKS_PER_SEC) - origClock; // <--add start
    if( runtClock > 2 ) {
		luaG_runerror(L, "Runtime length of 2 seconds exceeded.");
	} // <--add stop
    if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
        (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
      traceexec(L, pc);

As always, Your Mileage May Vary
Amended on Tue 29 Jan 2008 08:54 AM by ThomasWatts
Australia Forum Administrator #1
Another approach is to use a hook as described in this post "How to stop runaway scripts":

http://www.gammon.com.au/forum/bbshowpost.php?id=6534

The method described there can be used without recompiling Lua.
USA #2
Interesting, I had no idea that existed, I'll look into it.
Thanks Nick.

The above implemented in C inside call_lua_function:

static void luaCountHook(lua_State *L, lua_Debug *ar) {
	char buf[MAX_STRING_LENGTH];
	sprintf(buf, "Execution limits exceeded.\n");
	bug(buf);
	luaL_error(L, buf);
}

static long call_lua_function(lua_State *L, const char * fname, const int nArgs) {
	long iResult = FALSE;
	int error;
	lua_sethook(L, luaCountHook, LUA_MASKCOUNT, 100000);
	error = CallLuaWithTraceBack(L, nArgs, 0);
	lua_sethook(L, luaCountHook, 0, 0);
	if( error ) {
		bug("Error %d executing Lua function %s:\n %s\n", error, fname, lua_tostring(L, -1));
		lua_pop(L, 1); // pop error
	}
	if( lua_isnoneornil(L, -1) ) //treat both no return and nil the same
		iResult = LUA_NONE_OR_NIL;
	else if( lua_isnumber(L, -1) )
		iResult = (long) luaL_checknumber(L, -1);
	else if( lua_isboolean (L, -1) )
		iResult = lua_toboolean(L, -1);
	return iResult;
}


Not sure what to do with the 'lua_Debug *ar' yet, but I'll pass that on when I find out more.