From 8975782d9af137ebde5b98a3d6672000b51ece3d Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 25 Apr 2026 14:56:51 -0400 Subject: [PATCH] error: Alternate between two buffers in SDL_SetError. This way you can always safely use SDL_GetError() in your formatted string: ```c SDL_SetError("Couldn't open '%s': %s", filename, SDL_GetError()); ``` This problem was hidden on platforms that use the dynamic API, because it would format the new error string to a separate buffer first, to deal with the varargs entry point. Fixes #15456. (cherry picked from commit 559d226fc65a7191eca1a75e0646ee31e5a2ca9c) --- src/SDL_error.c | 37 +++++++++++++++++++++++-------------- src/SDL_error_c.h | 8 +++++++- src/thread/SDL_thread.c | 17 +++++++++++------ 3 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/SDL_error.c b/src/SDL_error.c index 618eb93d68..64c1bf06eb 100644 --- a/src/SDL_error.c +++ b/src/SDL_error.c @@ -43,22 +43,28 @@ bool SDL_SetErrorV(SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) if (fmt) { int result; SDL_error *error = SDL_GetErrBuf(true); + + // use the other slot for the new error, so if this does + // SDL_SetError("%s", SDL_GetError()), we don't have a problem. + const int current = error->current ? 0 : 1; + SDL_ErrorInfo *errinfo = &error->info[current]; + error->current = current; + + errinfo->error = SDL_ErrorCodeGeneric; + va_list ap2; - - error->error = SDL_ErrorCodeGeneric; - va_copy(ap2, ap); - result = SDL_vsnprintf(error->str, error->len, fmt, ap2); + result = SDL_vsnprintf(errinfo->str, errinfo->len, fmt, ap2); va_end(ap2); - if (result >= 0 && (size_t)result >= error->len && error->realloc_func) { + if (result >= 0 && (size_t)result >= errinfo->len && error->realloc_func) { size_t len = (size_t)result + 1; - char *str = (char *)error->realloc_func(error->str, len); + char *str = (char *)error->realloc_func(errinfo->str, len); if (str) { - error->str = str; - error->len = len; + errinfo->str = str; + errinfo->len = len; va_copy(ap2, ap); - (void)SDL_vsnprintf(error->str, error->len, fmt, ap2); + (void)SDL_vsnprintf(errinfo->str, errinfo->len, fmt, ap2); va_end(ap2); } } @@ -67,7 +73,7 @@ bool SDL_SetErrorV(SDL_PRINTF_FORMAT_STRING const char *fmt, va_list ap) // Note that there are many recoverable errors that may happen internally and // can be safely ignored if the public API doesn't return an error code. #if 0 - SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s", error->str); + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "%s", errinfo->str); #endif } @@ -82,9 +88,10 @@ const char *SDL_GetError(void) return ""; } - switch (error->error) { + const SDL_ErrorInfo *errinfo = &error->info[error->current]; + switch (errinfo->error) { case SDL_ErrorCodeGeneric: - return error->str; + return errinfo->str; case SDL_ErrorCodeOutOfMemory: return "Out of memory"; default: @@ -97,7 +104,8 @@ bool SDL_ClearError(void) SDL_error *error = SDL_GetErrBuf(false); if (error) { - error->error = SDL_ErrorCodeNone; + SDL_ErrorInfo *errinfo = &error->info[error->current]; + errinfo->error = SDL_ErrorCodeNone; } return true; } @@ -107,7 +115,8 @@ bool SDL_OutOfMemory(void) SDL_error *error = SDL_GetErrBuf(true); if (error) { - error->error = SDL_ErrorCodeOutOfMemory; + SDL_ErrorInfo *errinfo = &error->info[error->current]; + errinfo->error = SDL_ErrorCodeOutOfMemory; } return false; } diff --git a/src/SDL_error_c.h b/src/SDL_error_c.h index 6ea7703832..7d53783d88 100644 --- a/src/SDL_error_c.h +++ b/src/SDL_error_c.h @@ -34,11 +34,17 @@ typedef enum SDL_ErrorCodeOutOfMemory, } SDL_ErrorCode; -typedef struct SDL_error +typedef struct SDL_ErrorInfo { SDL_ErrorCode error; char *str; size_t len; +} SDL_ErrorInfo; + +typedef struct SDL_error +{ + SDL_ErrorInfo info[2]; // there are two, so you can do SDL_SetError("%s", SDL_GetError()) without stomping the buffer. + int current; SDL_realloc_func realloc_func; SDL_free_func free_func; } SDL_error; diff --git a/src/thread/SDL_thread.c b/src/thread/SDL_thread.c index efa1b72d4e..0d02bcd312 100644 --- a/src/thread/SDL_thread.c +++ b/src/thread/SDL_thread.c @@ -262,9 +262,12 @@ void SDL_Generic_QuitTLSData(void) static SDL_error *SDL_GetStaticErrBuf(void) { static SDL_error SDL_global_error; - static char SDL_global_error_str[128]; - SDL_global_error.str = SDL_global_error_str; - SDL_global_error.len = sizeof(SDL_global_error_str); + static char SDL_global_error_str1[128]; + static char SDL_global_error_str2[128]; + SDL_global_error.info[0].str = SDL_global_error_str1; + SDL_global_error.info[0].len = sizeof(SDL_global_error_str1); + SDL_global_error.info[1].str = SDL_global_error_str2; + SDL_global_error.info[1].len = sizeof(SDL_global_error_str2); return &SDL_global_error; } @@ -272,9 +275,11 @@ static SDL_error *SDL_GetStaticErrBuf(void) static void SDLCALL SDL_FreeErrBuf(void *data) { SDL_error *errbuf = (SDL_error *)data; - - if (errbuf->str) { - errbuf->free_func(errbuf->str); + if (errbuf->info[0].str) { + errbuf->free_func(errbuf->info[0].str); + } + if (errbuf->info[1].str) { + errbuf->free_func(errbuf->info[1].str); } errbuf->free_func(errbuf); }