mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-06-14 05:49:57 +00:00
redis: Fix CVE-2025-46818
Upstream-Status: Backport from https://github.com/redis/redis/commit/dccb672d838f05c940f040c27b74fde6fb47b2a7 Signed-off-by: Vijay Anusuri <vanusuri@mvista.com> Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
This commit is contained in:
committed by
Gyorgy Sarvari
parent
4450ab7430
commit
abe7f83cc6
@@ -0,0 +1,283 @@
|
||||
From dccb672d838f05c940f040c27b74fde6fb47b2a7 Mon Sep 17 00:00:00 2001
|
||||
From: Ozan Tezcan <ozantezcan@gmail.com>
|
||||
Date: Mon, 23 Jun 2025 12:10:12 +0300
|
||||
Subject: [PATCH] Lua script can be executed in the context of another user
|
||||
(CVE-2025-46818)
|
||||
|
||||
Upstream-Status: Backport [https://github.com/redis/redis/commit/dccb672d838f05c940f040c27b74fde6fb47b2a7]
|
||||
CVE: CVE-2025-46818
|
||||
Signed-off-by: Vijay Anusuri <vanusuri@mvista.com>
|
||||
---
|
||||
src/config.c | 1 +
|
||||
src/eval.c | 2 ++
|
||||
src/function_lua.c | 2 ++
|
||||
src/script_lua.c | 59 +++++++++++++++++++++++++++++----
|
||||
src/script_lua.h | 1 +
|
||||
src/server.h | 1 +
|
||||
tests/unit/scripting.tcl | 70 ++++++++++++++++++++++++++++++++++++++++
|
||||
7 files changed, 129 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/src/config.c b/src/config.c
|
||||
index bfb49ef..d232eaf 100644
|
||||
--- a/src/config.c
|
||||
+++ b/src/config.c
|
||||
@@ -3011,6 +3011,7 @@ standardConfig static_configs[] = {
|
||||
createBoolConfig("latency-tracking", NULL, MODIFIABLE_CONFIG, server.latency_tracking_enabled, 1, NULL, NULL),
|
||||
createBoolConfig("aof-disable-auto-gc", NULL, MODIFIABLE_CONFIG, server.aof_disable_auto_gc, 0, NULL, updateAofAutoGCEnabled),
|
||||
createBoolConfig("replica-ignore-disk-write-errors", NULL, MODIFIABLE_CONFIG, server.repl_ignore_disk_write_error, 0, NULL, NULL),
|
||||
+ createBoolConfig("lua-enable-deprecated-api", NULL, IMMUTABLE_CONFIG | HIDDEN_CONFIG, server.lua_enable_deprecated_api, 0, NULL, NULL),
|
||||
|
||||
/* String Configs */
|
||||
createStringConfig("aclfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.acl_filename, "", NULL, NULL),
|
||||
diff --git a/src/eval.c b/src/eval.c
|
||||
index a562335..f39ccc9 100644
|
||||
--- a/src/eval.c
|
||||
+++ b/src/eval.c
|
||||
@@ -261,6 +261,8 @@ void scriptingInit(int setup) {
|
||||
/* Recursively lock all tables that can be reached from the global table */
|
||||
luaSetTableProtectionRecursively(lua);
|
||||
lua_pop(lua, 1);
|
||||
+ /* Set metatables of basic types (string, number, nil etc.) readonly. */
|
||||
+ luaSetTableProtectionForBasicTypes(lua);
|
||||
|
||||
lctx.lua = lua;
|
||||
}
|
||||
diff --git a/src/function_lua.c b/src/function_lua.c
|
||||
index aedadb0..9450437 100644
|
||||
--- a/src/function_lua.c
|
||||
+++ b/src/function_lua.c
|
||||
@@ -490,6 +490,8 @@ int luaEngineInitEngine() {
|
||||
lua_enablereadonlytable(lua_engine_ctx->lua, -1, 1); /* protect the new global table */
|
||||
lua_replace(lua_engine_ctx->lua, LUA_GLOBALSINDEX); /* set new global table as the new globals */
|
||||
|
||||
+ /* Set metatables of basic types (string, number, nil etc.) readonly. */
|
||||
+ luaSetTableProtectionForBasicTypes(lua_engine_ctx->lua);
|
||||
|
||||
engine *lua_engine = zmalloc(sizeof(*lua_engine));
|
||||
*lua_engine = (engine) {
|
||||
diff --git a/src/script_lua.c b/src/script_lua.c
|
||||
index 33ed2aa..25a798f 100644
|
||||
--- a/src/script_lua.c
|
||||
+++ b/src/script_lua.c
|
||||
@@ -65,7 +65,6 @@ static char *redis_api_allow_list[] = {
|
||||
static char *lua_builtins_allow_list[] = {
|
||||
"xpcall",
|
||||
"tostring",
|
||||
- "getfenv",
|
||||
"setmetatable",
|
||||
"next",
|
||||
"assert",
|
||||
@@ -86,15 +85,16 @@ static char *lua_builtins_allow_list[] = {
|
||||
"loadstring",
|
||||
"ipairs",
|
||||
"_VERSION",
|
||||
- "setfenv",
|
||||
"load",
|
||||
"error",
|
||||
NULL,
|
||||
};
|
||||
|
||||
-/* Lua builtins which are not documented on the Lua documentation */
|
||||
-static char *lua_builtins_not_documented_allow_list[] = {
|
||||
+/* Lua builtins which are deprecated for sandboxing concerns */
|
||||
+static char *lua_builtins_deprecated[] = {
|
||||
"newproxy",
|
||||
+ "setfenv",
|
||||
+ "getfenv",
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -116,7 +116,6 @@ static char **allow_lists[] = {
|
||||
libraries_allow_list,
|
||||
redis_api_allow_list,
|
||||
lua_builtins_allow_list,
|
||||
- lua_builtins_not_documented_allow_list,
|
||||
lua_builtins_removed_after_initialization_allow_list,
|
||||
NULL,
|
||||
};
|
||||
@@ -1323,7 +1322,22 @@ static int luaNewIndexAllowList(lua_State *lua) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
- if (!*allow_l) {
|
||||
+
|
||||
+ int allowed = (*allow_l != NULL);
|
||||
+ /* If not explicitly allowed, check if it's a deprecated function. If so,
|
||||
+ * allow it only if 'lua_enable_deprecated_api' config is enabled. */
|
||||
+ int deprecated = 0;
|
||||
+ if (!allowed) {
|
||||
+ char **c = lua_builtins_deprecated;
|
||||
+ for (; *c; ++c) {
|
||||
+ if (strcmp(*c, variable_name) == 0) {
|
||||
+ deprecated = 1;
|
||||
+ allowed = server.lua_enable_deprecated_api ? 1 : 0;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (!allowed) {
|
||||
/* Search the value on the back list, if its there we know that it was removed
|
||||
* on purpose and there is no need to print a warning. */
|
||||
char **c = deny_list;
|
||||
@@ -1332,7 +1346,7 @@ static int luaNewIndexAllowList(lua_State *lua) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
- if (!*c) {
|
||||
+ if (!*c && !deprecated) {
|
||||
serverLog(LL_WARNING, "A key '%s' was added to Lua globals which is not on the globals allow list nor listed on the deny list.", variable_name);
|
||||
}
|
||||
} else {
|
||||
@@ -1384,6 +1398,37 @@ void luaSetTableProtectionRecursively(lua_State *lua) {
|
||||
}
|
||||
}
|
||||
|
||||
+/* Set the readonly flag on the metatable of basic types (string, nil etc.) */
|
||||
+void luaSetTableProtectionForBasicTypes(lua_State *lua) {
|
||||
+ static const int types[] = {
|
||||
+ LUA_TSTRING,
|
||||
+ LUA_TNUMBER,
|
||||
+ LUA_TBOOLEAN,
|
||||
+ LUA_TNIL,
|
||||
+ LUA_TFUNCTION,
|
||||
+ LUA_TTHREAD,
|
||||
+ LUA_TLIGHTUSERDATA
|
||||
+ };
|
||||
+
|
||||
+ for (size_t i = 0; i < sizeof(types) / sizeof(types[0]); i++) {
|
||||
+ /* Push a dummy value of the type to get its metatable */
|
||||
+ switch (types[i]) {
|
||||
+ case LUA_TSTRING: lua_pushstring(lua, ""); break;
|
||||
+ case LUA_TNUMBER: lua_pushnumber(lua, 0); break;
|
||||
+ case LUA_TBOOLEAN: lua_pushboolean(lua, 0); break;
|
||||
+ case LUA_TNIL: lua_pushnil(lua); break;
|
||||
+ case LUA_TFUNCTION: lua_pushcfunction(lua, NULL); break;
|
||||
+ case LUA_TTHREAD: lua_newthread(lua); break;
|
||||
+ case LUA_TLIGHTUSERDATA: lua_pushlightuserdata(lua, (void*)lua); break;
|
||||
+ }
|
||||
+ if (lua_getmetatable(lua, -1)) {
|
||||
+ luaSetTableProtectionRecursively(lua);
|
||||
+ lua_pop(lua, 1); /* pop metatable */
|
||||
+ }
|
||||
+ lua_pop(lua, 1); /* pop dummy value */
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void luaRegisterVersion(lua_State* lua) {
|
||||
lua_pushstring(lua,"REDIS_VERSION_NUM");
|
||||
lua_pushnumber(lua,REDIS_VERSION_NUM);
|
||||
diff --git a/src/script_lua.h b/src/script_lua.h
|
||||
index 4c2b348..d8a3688 100644
|
||||
--- a/src/script_lua.h
|
||||
+++ b/src/script_lua.h
|
||||
@@ -71,6 +71,7 @@ void luaRegisterGlobalProtectionFunction(lua_State *lua);
|
||||
void luaSetErrorMetatable(lua_State *lua);
|
||||
void luaSetAllowListProtection(lua_State *lua);
|
||||
void luaSetTableProtectionRecursively(lua_State *lua);
|
||||
+void luaSetTableProtectionForBasicTypes(lua_State *lua);
|
||||
void luaRegisterLogFunction(lua_State* lua);
|
||||
void luaRegisterVersion(lua_State* lua);
|
||||
void luaPushErrorBuff(lua_State *lua, sds err_buff);
|
||||
diff --git a/src/server.h b/src/server.h
|
||||
index 82e4db9..952135f 100644
|
||||
--- a/src/server.h
|
||||
+++ b/src/server.h
|
||||
@@ -1900,6 +1900,7 @@ struct redisServer {
|
||||
mstime_t busy_reply_threshold; /* Script / module timeout in milliseconds */
|
||||
int pre_command_oom_state; /* OOM before command (script?) was started */
|
||||
int script_disable_deny_script; /* Allow running commands marked "no-script" inside a script. */
|
||||
+ int lua_enable_deprecated_api; /* Config to enable deprecated api */
|
||||
/* Lazy free */
|
||||
int lazyfree_lazy_eviction;
|
||||
int lazyfree_lazy_expire;
|
||||
diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl
|
||||
index d2fd6da..58f2028 100644
|
||||
--- a/tests/unit/scripting.tcl
|
||||
+++ b/tests/unit/scripting.tcl
|
||||
@@ -1021,6 +1021,27 @@ start_server {tags {"scripting"}} {
|
||||
set _ $e
|
||||
} {*Attempt to modify a readonly table*}
|
||||
|
||||
+ test "Try trick readonly table on basic types metatable" {
|
||||
+ # Run the following scripts for basic types. Either getmetatable()
|
||||
+ # should return nil or the metatable must be readonly.
|
||||
+ set scripts {
|
||||
+ {getmetatable(nil).__index = function() return 1 end}
|
||||
+ {getmetatable('').__index = function() return 1 end}
|
||||
+ {getmetatable(123.222).__index = function() return 1 end}
|
||||
+ {getmetatable(true).__index = function() return 1 end}
|
||||
+ {getmetatable(function() return 1 end).__index = function() return 1 end}
|
||||
+ {getmetatable(coroutine.create(function() return 1 end)).__index = function() return 1 end}
|
||||
+ }
|
||||
+
|
||||
+ foreach code $scripts {
|
||||
+ catch {run_script $code 0} e
|
||||
+ assert {
|
||||
+ [string match "*attempt to index a nil value script*" $e] ||
|
||||
+ [string match "*Attempt to modify a readonly table*" $e]
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
test "Test loadfile are not available" {
|
||||
catch {
|
||||
run_script {
|
||||
@@ -1049,6 +1070,55 @@ start_server {tags {"scripting"}} {
|
||||
} {*Script attempted to access nonexistent global variable 'print'*}
|
||||
}
|
||||
|
||||
+# Start a new server to test lua-enable-deprecated-api config
|
||||
+foreach enabled {no yes} {
|
||||
+start_server [subst {tags {"scripting external:skip"} overrides {lua-enable-deprecated-api $enabled}}] {
|
||||
+ test "Test setfenv availability lua-enable-deprecated-api=$enabled" {
|
||||
+ catch {
|
||||
+ run_script {
|
||||
+ local f = function() return 1 end
|
||||
+ setfenv(f, {})
|
||||
+ return 0
|
||||
+ } 0
|
||||
+ } e
|
||||
+ if {$enabled} {
|
||||
+ assert_equal $e 0
|
||||
+ } else {
|
||||
+ assert_match {*Script attempted to access nonexistent global variable 'setfenv'*} $e
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ test "Test getfenv availability lua-enable-deprecated-api=$enabled" {
|
||||
+ catch {
|
||||
+ run_script {
|
||||
+ local f = function() return 1 end
|
||||
+ getfenv(f)
|
||||
+ return 0
|
||||
+ } 0
|
||||
+ } e
|
||||
+ if {$enabled} {
|
||||
+ assert_equal $e 0
|
||||
+ } else {
|
||||
+ assert_match {*Script attempted to access nonexistent global variable 'getfenv'*} $e
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ test "Test newproxy availability lua-enable-deprecated-api=$enabled" {
|
||||
+ catch {
|
||||
+ run_script {
|
||||
+ getmetatable(newproxy(true)).__gc = function() return 1 end
|
||||
+ return 0
|
||||
+ } 0
|
||||
+ } e
|
||||
+ if {$enabled} {
|
||||
+ assert_equal $e 0
|
||||
+ } else {
|
||||
+ assert_match {*Script attempted to access nonexistent global variable 'newproxy'*} $e
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+}
|
||||
+
|
||||
# Start a new server since the last test in this stanza will kill the
|
||||
# instance at all.
|
||||
start_server {tags {"scripting"}} {
|
||||
--
|
||||
2.25.1
|
||||
|
||||
@@ -28,6 +28,7 @@ SRC_URI = "http://download.redis.io/releases/${BP}.tar.gz \
|
||||
file://CVE-2025-32023.patch \
|
||||
file://CVE-2025-48367.patch \
|
||||
file://CVE-2025-46817.patch \
|
||||
file://CVE-2025-46818.patch \
|
||||
"
|
||||
SRC_URI[sha256sum] = "97065774d5fb8388eb0d8913458decfcb167d356e40d31dd01cd30c1cc391673"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user