Files
ReSkate/External/luau/VM/src/linit.cpp
T
AyeZeeBB 6d542536e1 Plugins: sandboxed Luau plugins in mods, phase 1 (the host, and the base, console and storage APIs)
A mod with a reskate-plugin.json is a plugin: Luau scripts that run in their own VM, with only
the reskate API ReSkate registers for them. Luau 0.741 is vendored in External/luau (the VM and
compiler only: no native code generation, no loadstring, io or C modules).

- Limits: 64 MB of memory, 50 ms a call, 2 ms a tick on average (over it for 3 s stops it),
  more than 5 errors in 10 s stops it, 50 log lines a second. A plugin over a limit cannot catch
  it in a pcall and carry on.
- API: reskate.log, on (load, unload, tick, level_loaded, level_unloading), timers, the level,
  its own package's files and JSON, require inside the package; console.command and print
  ("console"); storage get, set, add and keys ("storage"), 1 MB in
  %LOCALAPPDATA%\ReSkate\Plugins\<mod>\storage.json.
- Found every 2 s among the mods mods.json leaves on: switching a mod, or changing a plugin's
  files, loads, unloads or reloads it without a restart. A package holding a program is refused.
- plugin list, commands, reload, off and on in the console. A plugin cannot take a name of
  ReSkate's own commands.
- Tests: DINGOSDK_BUILD_PLUGIN_TESTS, including plugins written to break the sandbox.
2026-10-19 11:55:27 -05:00

147 lines
3.8 KiB
C++

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
#include "lualib.h"
#include "lstate.h"
#include <stdlib.h>
LUAU_FASTFLAG(LuauIntegerLibrary)
LUAU_FASTFLAG(DebugLuauUserDefinedClassesRuntime)
LUAU_FASTFLAGVARIABLE(LuauSandboxFreezesVectorMetatable)
static const luaL_Reg lualibs[] = {
{"", luaopen_base},
{LUA_COLIBNAME, luaopen_coroutine},
{LUA_TABLIBNAME, luaopen_table},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_DBLIBNAME, luaopen_debug},
{LUA_UTF8LIBNAME, luaopen_utf8},
{LUA_BITLIBNAME, luaopen_bit32},
{LUA_BUFFERLIBNAME, luaopen_buffer},
{LUA_VECLIBNAME, luaopen_vector},
{LUA_INTLIBNAME, luaopen_integer},
{NULL, NULL},
};
static const luaL_Reg lualibs_NOINTEGER[] = {
{"", luaopen_base},
{LUA_COLIBNAME, luaopen_coroutine},
{LUA_TABLIBNAME, luaopen_table},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_DBLIBNAME, luaopen_debug},
{LUA_UTF8LIBNAME, luaopen_utf8},
{LUA_BITLIBNAME, luaopen_bit32},
{LUA_BUFFERLIBNAME, luaopen_buffer},
{LUA_VECLIBNAME, luaopen_vector},
{NULL, NULL},
};
void luaL_openlibs(lua_State* L)
{
const luaL_Reg* lib;
if (FFlag::LuauIntegerLibrary)
lib = lualibs;
else
lib = lualibs_NOINTEGER;
for (; lib->func; lib++)
{
lua_pushcfunction(L, lib->func, NULL);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}
if (FFlag::DebugLuauUserDefinedClassesRuntime)
{
lua_pushcfunction(L, luaopen_class, NULL);
lua_pushstring(L, LUA_CLASSLIBNAME);
lua_call(L, 1, 0);
}
}
void luaL_sandbox(lua_State* L)
{
// set all libraries to read-only
lua_pushnil(L);
while (lua_next(L, LUA_GLOBALSINDEX) != 0)
{
if (lua_istable(L, -1))
lua_setreadonly(L, -1, true);
lua_pop(L, 1);
}
// set all builtin metatables to read-only
lua_pushliteral(L, "");
if (lua_getmetatable(L, -1))
{
lua_setreadonly(L, -1, true);
lua_pop(L, 2);
}
else
{
lua_pop(L, 1);
}
if (FFlag::LuauSandboxFreezesVectorMetatable)
{
#if LUA_VECTOR_SIZE == 4
lua_pushvector(L, 0.0f, 0.0f, 0.0f, 0.0f);
#else
lua_pushvector(L, 0.0f, 0.0f, 0.0f);
#endif
if (lua_getmetatable(L, -1))
{
lua_setreadonly(L, -1, true);
lua_pop(L, 2);
}
else
{
lua_pop(L, 1);
}
}
// set globals to readonly and activate safeenv since the env is immutable
lua_setreadonly(L, LUA_GLOBALSINDEX, true);
lua_setsafeenv(L, LUA_GLOBALSINDEX, true);
}
void luaL_sandboxthread(lua_State* L)
{
// create new global table that proxies reads to original table
lua_newtable(L);
lua_newtable(L);
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setfield(L, -2, "__index");
lua_setreadonly(L, -1, true);
lua_setmetatable(L, -2);
// we can set safeenv now although it's important to set it to false if code is loaded twice into the thread
lua_replace(L, LUA_GLOBALSINDEX);
lua_setsafeenv(L, LUA_GLOBALSINDEX, true);
}
static void* l_alloc(void* ud, void* ptr, size_t osize, size_t nsize)
{
(void)ud;
(void)osize;
if (nsize == 0)
{
free(ptr);
return NULL;
}
else
return realloc(ptr, nsize);
}
lua_State* luaL_newstate(void)
{
return lua_newstate(l_alloc, NULL);
}