## 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:
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:
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 >=:
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).
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