From f3c1484089d05f5bc1ae8c3a81cef0ef114f5ddd Mon Sep 17 00:00:00 2001 From: "Diego N." Date: Wed, 6 May 2026 20:25:10 -0500 Subject: [PATCH 1/2] fix(security): sanitize StaticConstructObject parameters --- UE4SS/src/Mod/LuaMod.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/UE4SS/src/Mod/LuaMod.cpp b/UE4SS/src/Mod/LuaMod.cpp index 01c95a7af..14b990850 100644 --- a/UE4SS/src/Mod/LuaMod.cpp +++ b/UE4SS/src/Mod/LuaMod.cpp @@ -1953,16 +1953,34 @@ No overload found for function 'StaticConstructObject'. // Change this to userdata if support for 'FObjectInstancingGraph' is ever added void* param_instance_graph{}; - if (lua.is_integer()) + if (lua.is_nil()) + { + lua.discard_value(); + } + else if (lua.is_userdata()) + { + param_instance_graph = lua_touserdata(lua.get_lua_state(), 1); + lua.discard_value(); + } + else if (lua.get_stack_size() > 0) { - param_instance_graph = reinterpret_cast(static_cast(lua.get_integer())); + lua.throw_error("StaticConstructObject: InstanceGraph must be lightuserdata or nil"); } // Change this to userdata if support for 'UPackage' is ever added void* param_external_package{}; - if (lua.is_integer()) + if (lua.is_nil()) + { + lua.discard_value(); + } + else if (lua.is_userdata()) + { + param_external_package = lua_touserdata(lua.get_lua_state(), 1); + lua.discard_value(); + } + else if (lua.get_stack_size() > 0) { - param_external_package = reinterpret_cast(static_cast(lua.get_integer())); + lua.throw_error("StaticConstructObject: ExternalPackage must be lightuserdata or nil"); } // void* param_subobject_overrides{}; From 605a26f33b6a40d93530117125f009ff30222900 Mon Sep 17 00:00:00 2001 From: "Diego N." Date: Thu, 7 May 2026 12:11:35 -0500 Subject: [PATCH 2/2] fix(LuaMod): fix StaticConstructObject stack indexing (Issue #1149) Rewrites StaticConstructObject to use absolute stack indices and non-consuming logic. This resolves parameter misalignment issues when optional parameters are omitted or of incorrect types, which was causing crashes in production. --- UE4SS/src/Mod/LuaMod.cpp | 94 ++++++++++++++++++++-------------------- 1 file changed, 48 insertions(+), 46 deletions(-) diff --git a/UE4SS/src/Mod/LuaMod.cpp b/UE4SS/src/Mod/LuaMod.cpp index 14b990850..c5f54cb2b 100644 --- a/UE4SS/src/Mod/LuaMod.cpp +++ b/UE4SS/src/Mod/LuaMod.cpp @@ -1893,94 +1893,96 @@ No overload found for function 'StaticConstructObject'. ))"}; + lua_State* L = lua.get_lua_state(); + int num_args = lua_gettop(L); + // For now, we're assuming that if there's userdata, that userdata is of the correct underlying type - if (!lua.is_userdata()) + if (num_args < 1 || !lua.is_userdata(1)) { lua.throw_error(error_overload_not_found); } - Unreal::UClass* param_class = lua.get_userdata().get_remote_cpp_object(); + Unreal::UClass* param_class = lua.get_userdata(1, true).get_remote_cpp_object(); - if (!lua.is_userdata()) + if (num_args < 2 || !lua.is_userdata(2)) { lua.throw_error(error_overload_not_found); } - Unreal::UObject* param_outer = lua.get_userdata().get_remote_cpp_object(); + Unreal::UObject* param_outer = lua.get_userdata(2, true).get_remote_cpp_object(); Unreal::FName param_name; - if (lua.is_userdata()) + if (num_args >= 3) { - param_name = lua.get_userdata().get_local_cpp_object(); - } - else if (lua.is_integer()) - { - param_name = Unreal::FName(lua.get_integer()); - } - else - { - param_name = Unreal::FName(static_cast(0)); + if (lua.is_userdata(3)) + { + param_name = lua.get_userdata(3, true).get_local_cpp_object(); + } + else if (lua.is_integer(3)) + { + param_name = Unreal::FName(lua_tointeger(L, 3)); + } + else + { + param_name = Unreal::FName(static_cast(0)); + } } Unreal::EObjectFlags param_set_flags{}; - if (lua.is_integer()) + if (num_args >= 4 && lua.is_integer(4)) { - param_set_flags = static_cast(lua.get_integer()); + param_set_flags = static_cast(lua_tointeger(L, 4)); } Unreal::EInternalObjectFlags param_internal_set_flags{}; - if (lua.is_integer()) + if (num_args >= 5 && lua.is_integer(5)) { - param_internal_set_flags = static_cast(lua.get_integer()); + param_internal_set_flags = static_cast(lua_tointeger(L, 5)); } // The rest are all optional parameters bool param_copy_transients_from_class_defaults{}; - if (lua.is_bool()) + if (num_args >= 6 && lua.is_bool(6)) { - param_copy_transients_from_class_defaults = lua.get_bool(); + param_copy_transients_from_class_defaults = lua_toboolean(L, 6); } bool param_assume_template_is_archetype{}; - if (lua.is_bool()) + if (num_args >= 7 && lua.is_bool(7)) { - param_assume_template_is_archetype = lua.get_bool(); + param_assume_template_is_archetype = lua_toboolean(L, 7); } Unreal::UObject* param_template{}; - if (lua.is_userdata()) + if (num_args >= 8 && lua.is_userdata(8)) { - param_template = lua.get_userdata().get_remote_cpp_object(); + param_template = lua.get_userdata(8, true).get_remote_cpp_object(); } // Change this to userdata if support for 'FObjectInstancingGraph' is ever added void* param_instance_graph{}; - if (lua.is_nil()) + if (num_args >= 9) { - lua.discard_value(); - } - else if (lua.is_userdata()) - { - param_instance_graph = lua_touserdata(lua.get_lua_state(), 1); - lua.discard_value(); - } - else if (lua.get_stack_size() > 0) - { - lua.throw_error("StaticConstructObject: InstanceGraph must be lightuserdata or nil"); + if (lua_islightuserdata(L, 9)) + { + param_instance_graph = lua_touserdata(L, 9); + } + else if (!lua_isnil(L, 9)) + { + lua.throw_error("StaticConstructObject: InstanceGraph must be lightuserdata or nil"); + } } // Change this to userdata if support for 'UPackage' is ever added void* param_external_package{}; - if (lua.is_nil()) + if (num_args >= 10) { - lua.discard_value(); - } - else if (lua.is_userdata()) - { - param_external_package = lua_touserdata(lua.get_lua_state(), 1); - lua.discard_value(); - } - else if (lua.get_stack_size() > 0) - { - lua.throw_error("StaticConstructObject: ExternalPackage must be lightuserdata or nil"); + if (lua_islightuserdata(L, 10)) + { + param_external_package = lua_touserdata(L, 10); + } + else if (!lua_isnil(L, 10)) + { + lua.throw_error("StaticConstructObject: ExternalPackage must be lightuserdata or nil"); + } } // void* param_subobject_overrides{};