SDL: error: Alternate between two buffers in SDL_SetError. (89757)

From 8975782d9af137ebde5b98a3d6672000b51ece3d Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
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 | 35 +++++++++++++++++++++±------------
src/SDL_error_c.h | 8 ++++++±
src/thread/SDL_thread.c | 17 ++++++++++±-----
3 files changed, 40 insertions(+), 20 deletions(-)

diff --git a/src/SDL_error.c b/src/SDL_error.c
index 618eb93d68def..64c1bf06ebac9 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);

  •    va_list ap2;
    
  •    error->error = SDL_ErrorCodeGeneric;
    
  •    // 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;
       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 6ea770383278f..7d53783d88fc1 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 efa1b72d4eb05..0d02bcd312573 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);
    }