From 836a1a0b0204176d4f2a03de6f6ca2ad1d7b36f9 Mon Sep 17 00:00:00 2001 From: Mikael Hermansson Date: Tue, 22 Jul 2025 09:59:34 +0200 Subject: [PATCH] Prevent infinite recursion during printing --- core/core_bind.cpp | 12 ++---------- core/core_bind.h | 2 -- core/error/error_macros.cpp | 38 ++++++++++++++++++++++++++++++++++-- core/string/print_string.cpp | 32 ++++++++++++++++++++++++++++++ 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/core/core_bind.cpp b/core/core_bind.cpp index c08ae987b0..e1f939e384 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -233,12 +233,10 @@ void Logger::log_message(const String &p_text, bool p_error) { ////// OS ////// void OS::LoggerBind::logv(const char *p_format, va_list p_list, bool p_err) { - if (!should_log(p_err) || is_logging) { + if (!should_log(p_err)) { return; } - is_logging = true; - constexpr int static_buf_size = 1024; char static_buf[static_buf_size] = { '\0' }; char *buf = static_buf; @@ -260,12 +258,10 @@ void OS::LoggerBind::logv(const char *p_format, va_list p_list, bool p_err) { if (len >= static_buf_size) { Memory::free_static(buf); } - - is_logging = false; } void OS::LoggerBind::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector> &p_script_backtraces) { - if (!should_log(true) || is_logging) { + if (!should_log(true)) { return; } @@ -275,13 +271,9 @@ void OS::LoggerBind::log_error(const char *p_function, const char *p_file, int p backtraces[i] = p_script_backtraces[i]; } - is_logging = true; - for (Ref &logger : loggers) { logger->log_error(p_function, p_file, p_line, p_code, p_rationale, p_editor_notify, CoreBind::Logger::ErrorType(p_type), backtraces); } - - is_logging = false; } PackedByteArray OS::get_entropy(int p_bytes) { diff --git a/core/core_bind.h b/core/core_bind.h index 9d6e3ee5b3..fa6fe88172 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -147,8 +147,6 @@ class OS : public Object { mutable HashMap feature_cache; class LoggerBind : public ::Logger { - inline static thread_local bool is_logging = false; - public: LocalVector> loggers; diff --git a/core/error/error_macros.cpp b/core/error/error_macros.cpp index ad53d13344..9734c79790 100644 --- a/core/error/error_macros.cpp +++ b/core/error/error_macros.cpp @@ -43,6 +43,19 @@ #endif static ErrorHandlerList *error_handler_list = nullptr; +static thread_local bool is_printing_error = false; + +static void _err_print_fallback(const char *p_function, const char *p_file, int p_line, const char *p_error_details, ErrorHandlerType p_type, bool p_reentrance) { + if (p_reentrance) { + fprintf(stderr, "While attempting to print an error, another error was printed:\n"); + } + + fprintf(stderr, "%s: %s\n", _error_handler_type_string(p_type), p_error_details); + + if (p_function && p_file) { + fprintf(stderr, " at: %s (%s:%i)\n", p_function, p_file, p_line); + } +} void add_error_handler(ErrorHandlerList *p_handler) { // If p_handler is already in error_handler_list @@ -91,12 +104,21 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co // Main error printing function. void _err_print_error(const char *p_function, const char *p_file, int p_line, const char *p_error, const char *p_message, bool p_editor_notify, ErrorHandlerType p_type) { + if (is_printing_error) { + // Fallback if we're already printing an error, to prevent infinite recursion. + const char *err_details = (p_message && *p_message) ? p_message : p_error; + _err_print_fallback(p_function, p_file, p_line, err_details, p_type, true); + return; + } + + is_printing_error = true; + if (OS::get_singleton()) { OS::get_singleton()->print_error(p_function, p_file, p_line, p_error, p_message, p_editor_notify, (Logger::ErrorType)p_type, ScriptServer::capture_script_backtraces(false)); } else { // Fallback if errors happen before OS init or after it's destroyed. const char *err_details = (p_message && *p_message) ? p_message : p_error; - fprintf(stderr, "%s: %s\n at: %s (%s:%i)\n", _error_handler_type_string(p_type), err_details, p_function, p_file, p_line); + _err_print_fallback(p_function, p_file, p_line, err_details, p_type, false); } _global_lock(); @@ -108,6 +130,8 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co } _global_unlock(); + + is_printing_error = false; } // For printing errors when we may crash at any point, so we must flush ASAP a lot of lines @@ -116,11 +140,19 @@ void _err_print_error(const char *p_function, const char *p_file, int p_line, co void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type) { const char *err_details = p_error.utf8().get_data(); + if (is_printing_error) { + // Fallback if we're already printing an error, to prevent infinite recursion. + _err_print_fallback(nullptr, nullptr, 0, err_details, p_type, true); + return; + } + + is_printing_error = true; + if (OS::get_singleton()) { OS::get_singleton()->printerr("%s: %s\n", _error_handler_type_string(p_type), err_details); } else { // Fallback if errors happen before OS init or after it's destroyed. - fprintf(stderr, "%s: %s\n", _error_handler_type_string(p_type), err_details); + _err_print_fallback(nullptr, nullptr, 0, err_details, p_type, false); } _global_lock(); @@ -132,6 +164,8 @@ void _err_print_error_asap(const String &p_error, ErrorHandlerType p_type) { } _global_unlock(); + + is_printing_error = false; } // Errors with message. (All combinations of p_error and p_message as String or char*.) diff --git a/core/string/print_string.cpp b/core/string/print_string.cpp index b57f7a0f70..13cd621f15 100644 --- a/core/string/print_string.cpp +++ b/core/string/print_string.cpp @@ -34,6 +34,11 @@ #include "core/os/os.h" static PrintHandlerList *print_handler_list = nullptr; +static thread_local bool is_printing = false; + +static void __print_fallback(const String &p_string, bool p_err) { + fprintf(p_err ? stderr : stdout, "While attempting to print a message, another message was printed:\n%s\n", p_string.utf8().get_data()); +} void add_print_handler(PrintHandlerList *p_handler) { _global_lock(); @@ -71,6 +76,13 @@ void __print_line(const String &p_string) { return; } + if (is_printing) { + __print_fallback(p_string, false); + return; + } + + is_printing = true; + OS::get_singleton()->print("%s\n", p_string.utf8().get_data()); _global_lock(); @@ -81,6 +93,8 @@ void __print_line(const String &p_string) { } _global_unlock(); + + is_printing = false; } void __print_line_rich(const String &p_string) { @@ -263,6 +277,13 @@ void __print_line_rich(const String &p_string) { } output += "\u001b[0m"; // Reset. + if (is_printing) { + __print_fallback(output, false); + return; + } + + is_printing = true; + OS::get_singleton()->print_rich("%s\n", output.utf8().get_data()); _global_lock(); @@ -273,6 +294,8 @@ void __print_line_rich(const String &p_string) { } _global_unlock(); + + is_printing = false; } void print_error(const String &p_string) { @@ -280,6 +303,13 @@ void print_error(const String &p_string) { return; } + if (is_printing) { + __print_fallback(p_string, true); + return; + } + + is_printing = true; + OS::get_singleton()->printerr("%s\n", p_string.utf8().get_data()); _global_lock(); @@ -290,6 +320,8 @@ void print_error(const String &p_string) { } _global_unlock(); + + is_printing = false; } bool is_print_verbose_enabled() {