Roblox's Lua/Vm and how it handles lua_pcallk/ypcall

Right now I’m working on a Rust Roblox lua that has the proper security as normal Roblox does (lemur is pure Lua which means if can be broken out of) and I understand the code is ran in a sort of VM. But how does it handle lua_pcallk if it’s not in 5.1.* but only in 5.2 and 5.3? I’m thinking these are the different methods they could take:

  1. Create lua_pcallk in the c file/vm
  2. Purely in Lua via thread magic

I would like to stick to how Roblox handles this to keep it as accurate as possible. Just a side question too. What else does Roblox change about the c files in order to add things from 5.2 and 5.3?

1 Like

Roblox has heavily modified the Lua language in C, even introducing additional bytecode instructions. To my understanding, pcallk isn’t in RBXLua either. While I was an intern there we ran into issues due to not being able to yield inside of a pcall. We worked around it and I’m not sure if a better solution was ever implemented. To answer your question, all the changes to Lua were made in C/C++ code.

However, a Lua-based implementation can be secure with the restrictions placed on Lua running in Roblox (namely restricting access to the debug library). See my sandbox implementation here:

For it to work properly, this module must be required at the top of every source file.

1 Like

Thank you for the response! But as a quick question would it be better for me to add the lua_callk, lua_pcallk, (lua_yieldk?) to the lua 5.1.5 c files in order to make this an easier time for me? (or even switch to 5.2/5.3 and add compatibility?) or should I stick to how Roblox deals with this and add it to the environment?

Also I would like to stay away from lua sandboxing as much as possible due to how finicky sandboxing and the weirdest of ways of getting out of sandboxes. For example coroutine.yield() has weird behaviors and _G allowed things aswell.

Your goal is to create an environment identical to Roblox, correct? Adding a LuaC function to perform the duties of lua_callk and lua_pcallk would be pretty easy if you are willing to use your own error handler in C and redirect to the correct error handler set by pcallk if an error occurs. I haven’t used lua_yieldk, so I can’t speak to that. Using a higher version of lua would bring in a lot of sometimes subtle changes so I don’t think it is worth your time.

As for the sandbox, the only downside is that it is slower. I’ve theoretically covered every possible way to break out of it besides accessing string properties (since the hidden string metatable cannot be changed and I didn’t want to wrap every string, but it is harmless anyways). Rawget, rawset, setmetatable, getmetatable, type, setfenv, getfenv, Roblox userdata properties / methods / services, _G, shared, and every other function works properly and cannot be used to escape the sandbox. This is because each of those functions / tables / userdatas is also wrapped. But I agree, a sandbox like this is more of a bandaid and temporary solution than a long term one.

4 Likes