From 4a265554aeed885bfba42d425024d34cbb779a8c Mon Sep 17 00:00:00 2001 From: "debing.sun" Date: Thu, 22 Feb 2024 17:29:52 +0800 Subject: [PATCH] Expose lua os.clock() api (#12971) Implement #12699 This PR exposing Lua os.clock() api for getting the elapsed time of Lua code execution. Using: ```lua local start = os.clock() ... do something ... local elpased = os.clock() - start ``` --------- Co-authored-by: Meir Shpilraien (Spielrein) Co-authored-by: Madelyn Olson <34459052+madolson@users.noreply.github.com> --- deps/lua/src/loslib.c | 9 +++++++- src/script_lua.c | 3 ++- tests/unit/scripting.tcl | 46 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/deps/lua/src/loslib.c b/deps/lua/src/loslib.c index da06a572a..403f41634 100644 --- a/deps/lua/src/loslib.c +++ b/deps/lua/src/loslib.c @@ -234,10 +234,17 @@ static const luaL_Reg syslib[] = { /* }====================================================== */ +#define UNUSED(V) ((void) V) +/* Only a subset is loaded currently, for sandboxing concerns. */ +static const luaL_Reg sandbox_syslib[] = { + {"clock", os_clock}, + {NULL, NULL} +}; LUALIB_API int luaopen_os (lua_State *L) { - luaL_register(L, LUA_OSLIBNAME, syslib); + UNUSED(syslib); + luaL_register(L, LUA_OSLIBNAME, sandbox_syslib); return 1; } diff --git a/src/script_lua.c b/src/script_lua.c index a98b1da1b..eca21d60c 100644 --- a/src/script_lua.c +++ b/src/script_lua.c @@ -51,6 +51,7 @@ static char *libraries_allow_list[] = { "math", "table", "struct", + "os", NULL, }; @@ -1232,6 +1233,7 @@ static void luaLoadLibraries(lua_State *lua) { luaLoadLib(lua, LUA_STRLIBNAME, luaopen_string); luaLoadLib(lua, LUA_MATHLIBNAME, luaopen_math); luaLoadLib(lua, LUA_DBLIBNAME, luaopen_debug); + luaLoadLib(lua, LUA_OSLIBNAME, luaopen_os); luaLoadLib(lua, "cjson", luaopen_cjson); luaLoadLib(lua, "struct", luaopen_struct); luaLoadLib(lua, "cmsgpack", luaopen_cmsgpack); @@ -1239,7 +1241,6 @@ static void luaLoadLibraries(lua_State *lua) { #if 0 /* Stuff that we don't load currently, for sandboxing concerns. */ luaLoadLib(lua, LUA_LOADLIBNAME, luaopen_package); - luaLoadLib(lua, LUA_OSLIBNAME, luaopen_os); #endif } diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl index 799a41f71..5805b563c 100644 --- a/tests/unit/scripting.tcl +++ b/tests/unit/scripting.tcl @@ -620,6 +620,52 @@ start_server {tags {"scripting"}} { [run_script {return redis.sha1hex('Pizza & Mandolino')} 0] } {da39a3ee5e6b4b0d3255bfef95601890afd80709 74822d82031af7493c20eefa13bd07ec4fada82f} + test "Measures elapsed time os.clock()" { + set escaped [run_script { + local start = os.clock() + while os.clock() - start < 1 do end + return {double = os.clock() - start} + } 0] + assert_morethan_equal $escaped 1 ;# 1 second + } + + test "Prohibit dangerous lua methods in sandbox" { + assert_equal "" [run_script { + local allowed_methods = {"clock"} + -- Find a value from a tuple and return the position. + local indexOf = function(tuple, value) + for i, v in ipairs(tuple) do + if v == value then return i end + end + return nil + end + -- Check for disallowed methods and verify all allowed methods exist. + -- If an allowed method is found, it's removed from 'allowed_methods'. + -- If 'allowed_methods' is empty at the end, all allowed methods were found. + for key, value in pairs(os) do + local index = indexOf(allowed_methods, key) + if index == nil or type(value) ~= "function" then + return "Disallowed "..type(value)..":"..key + end + table.remove(allowed_methods, index) + end + if #allowed_methods ~= 0 then + return "Expected method not found: "..table.concat(allowed_methods, ",") + end + return "" + } 0] + } + + test "Verify execution of prohibit dangerous Lua methods will fail" { + assert_error {ERR *attempt to call field 'execute'*} {run_script {os.execute()} 0} + assert_error {ERR *attempt to call field 'exit'*} {run_script {os.exit()} 0} + assert_error {ERR *attempt to call field 'getenv'*} {run_script {os.getenv()} 0} + assert_error {ERR *attempt to call field 'remove'*} {run_script {os.remove()} 0} + assert_error {ERR *attempt to call field 'rename'*} {run_script {os.rename()} 0} + assert_error {ERR *attempt to call field 'setlocale'*} {run_script {os.setlocale()} 0} + assert_error {ERR *attempt to call field 'tmpname'*} {run_script {os.tmpname()} 0} + } + test {Globals protection reading an undeclared global variable} { catch {run_script {return a} 0} e set e