Please find attached a small fix for an off-by-one bug in the ISO C95+
`__mingw_vswprintf` path (`mingw_vsnprintf.c` with `__BUILD_WIDEAPI_ISO`).

## Problem

After commit c85d64d8c ("crt: Add support for C95 (v)swprintf() functions
when __USE_MINGW_ANSI_STDIO=1"), `swprintf(buf, n, ...)` returns -1 when
the formatted output exactly fills `n - 1` wide characters (plus the
terminator), even though ISO C95+ requires -1 only when **n or more** data
wide characters are needed.

Example:

```c
wchar_t buf[16];
int ret = swprintf(buf, 10, L".%8x", 0xd8c7bc89U);
/* ret is -1, but buf contains L".d8c7bc89" (9 chars + null) */
```

This showed up in the wild when statically linking code that formats a
9-character AppID hash suffix into a 10-wchar buffer (e.g. msys2-launcher).

## Cause

In `__vsnprintf`, `length` is decremented once before calling `__pformat`
(to reserve space for the terminator). The ISO error check then used:

```c
if (retval >= (int) length)
    retval = -1;
```

When output length equals `n - 1`, `retval == length`, so a successful
exact fit is incorrectly treated as an error.

## Fix

Use `>` instead of `>=`:

```c
if (retval > (int) length)
    retval = -1;
```

## Test

Extended `testcases/t_swprintf_tmpl.h` with:

- `swprintf(buf, 10, L".%8x", ...)` must return 9 on success
- `swprintf(buf, 9, L".%8x", ...)` must still return -1 on truncation

`make check` in testcases passes with this patch (verified via `t_swprintf1`).

## Patch

Commit: `29fe00be14f74a749aab86d3d0704d4580ed8374`

Files changed:

- `mingw-w64-crt/stdio/mingw_vsnprintf.c`
- `mingw-w64-crt/testcases/t_swprintf_tmpl.h`


-- 
         此致
礼
罗勇刚
Yours
    sincerely,
Yonggang Luo
From 65e3bcb5e74d64a774a1a2b277f2f9d28e341881 Mon Sep 17 00:00:00 2001
From: Yonggang Luo <[email protected]>
Date: Wed, 24 Jun 2026 02:06:57 +0800
Subject: [PATCH] crt: Fix ISO vswprintf exact-fit off-by-one error check.

- In __mingw_vswprintf, compare retval against n-1 with > not >= after the
  buffer size decrement; ISO C95+ should fail only when n or more data wide
  chars are needed.
- Extend t_swprintf_tmpl.h with an exact-fit swprintf(buf, 10, ...) case and
  a truncated n=9 failure case to catch regressions.

Signed-off-by: Yonggang Luo <[email protected]>
---
 mingw-w64-crt/stdio/mingw_vsnprintf.c     |  7 +++----
 mingw-w64-crt/testcases/t_swprintf_tmpl.h | 18 ++++++++++++++++++
 2 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/mingw-w64-crt/stdio/mingw_vsnprintf.c 
b/mingw-w64-crt/stdio/mingw_vsnprintf.c
index b9c357862..dd2d81ea8 100644
--- a/mingw-w64-crt/stdio/mingw_vsnprintf.c
+++ b/mingw-w64-crt/stdio/mingw_vsnprintf.c
@@ -57,11 +57,10 @@ int __cdecl __vsnprintf(APICHAR *buf, size_t length, const 
APICHAR *fmt, va_list
   buf[retval < (int) length ? retval : (int)length] = '\0';
 
 #if defined(__BUILD_WIDEAPI) && defined(__BUILD_WIDEAPI_ISO)
-  /* For wide api ISO C95+ vswprintf() when requested length
-   * is equal or larger than buffer length, returns negative
-   * value as required by ISO C95+.
+  /* ISO C95+ fails when n or more data wide chars are needed.  length was
+   * already decremented once for the terminator, so use > not >= here.
    */
-  if( retval >= (int) length )
+  if( retval > (int) length )
     retval = -1;
 #endif
 
diff --git a/mingw-w64-crt/testcases/t_swprintf_tmpl.h 
b/mingw-w64-crt/testcases/t_swprintf_tmpl.h
index a12cba9a2..5fe332645 100644
--- a/mingw-w64-crt/testcases/t_swprintf_tmpl.h
+++ b/mingw-w64-crt/testcases/t_swprintf_tmpl.h
@@ -19,5 +19,23 @@ int main() {
     fprintf(stderr, "\n");
     return 1;
   }
+
+  /* ISO C95+: output of 9 wide chars plus terminator must succeed when n=10.
+   * Regression for __mingw_vswprintf exact-fit check (retval == n-1). */
+  {
+    wchar_t exact[16] = L"XXXXXXXXXXXXXXXX";
+    ret = swprintf(exact, 10, L".%8x", 0xd8c7bc89U);
+    if (ret != 9 || wcscmp(exact, L".d8c7bc89") != 0) {
+      fprintf(stderr, "exact-fit n=10: ret expected=9 got=%d\n", ret);
+      fprintf(stderr, "exact-fit buffer: %ls\n", exact);
+      return 1;
+    }
+    ret = swprintf(exact, 9, L".%8x", 0xd8c7bc89U);
+    if (ret >= 0) {
+      fprintf(stderr, "exact-fit n=9: ret expected=<0 got=%d\n", ret);
+      return 1;
+    }
+  }
+
   return 0;
 }
-- 
2.52.0.windows.1

_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to