> Another way would be to make a minor interpreter modification to have it count byte code instructions executed
Lua provides the LUA_MASKCOUNT hook type since at least Lua 5.1, which calls the hook every N instructions. See https://www.lua.org/manual/5.1/manual.html#lua_sethook and https://www.lua.org/manual/5.4/manual.html#lua_sethook
The following prints 1 through 10 and then exits using Lua 5.3 and 5.4:
local i = 0
debug.sethook(function ()
i = i + 1
print(i)
if i >= 10 then
os.exit()
end
end, "", 1)
while true do
-- nothing
end
I've never used hooks in practice, though, so I may be missing some caveats beyond the obvious.
Regarding time, IIRC the Lua implementation very carefully implements lua_sethook (and related internals) such that you can safely install a hook from a signal handler, at least on common hardware like x86, ARM, etc where pointer assignments are naturally atomic. It can be tricky to make your lua_State context available to a signal handler, though.
Also, unless you isolate the process using system facilities, Lua probably isn't suited to running potentially malicious code as the VM doesn't see enough eyeballs. Looking through the bug history at https://www.lua.org/bugs.html it's apparent that even if you disable all APIs there are occasionally exploitable bugs in the interpreter. I suppose the same is true in practice regarding, e.g., Node--many more eyeballs but the JIT engines are so much more complex that you end up in the same place. (The jury is still out on dedicated WebAssembly engines, but JIT engines are notoriously complex and "memory safe" languages don't help with JIT bugs.)