Skip to content

Prevent infinite recursion during printing #108860

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions core/core_bind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Ref<ScriptBacktrace>> &p_script_backtraces) {
if (!should_log(true) || is_logging) {
if (!should_log(true)) {
return;
}

Expand All @@ -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<CoreBind::Logger> &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) {
Expand Down
2 changes: 0 additions & 2 deletions core/core_bind.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,6 @@ class OS : public Object {
mutable HashMap<String, bool> feature_cache;

class LoggerBind : public ::Logger {
inline static thread_local bool is_logging = false;

public:
LocalVector<Ref<CoreBind::Logger>> loggers;

Expand Down
38 changes: 36 additions & 2 deletions core/error/error_macros.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand All @@ -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
Expand All @@ -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();
Expand All @@ -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*.)
Expand Down
32 changes: 32 additions & 0 deletions core/string/print_string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand All @@ -81,6 +93,8 @@ void __print_line(const String &p_string) {
}

_global_unlock();

is_printing = false;
}

void __print_line_rich(const String &p_string) {
Expand Down Expand Up @@ -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();
Expand All @@ -273,13 +294,22 @@ void __print_line_rich(const String &p_string) {
}

_global_unlock();

is_printing = false;
}

void print_error(const String &p_string) {
if (!CoreGlobals::print_error_enabled) {
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();
Expand All @@ -290,6 +320,8 @@ void print_error(const String &p_string) {
}

_global_unlock();

is_printing = false;
}

bool is_print_verbose_enabled() {
Expand Down