On musl libc, test-vasnwprintf-posix fails. This is due to two musl libc bugs: https://www.openwall.com/lists/musl/2023/03/19/1 https://www.openwall.com/lists/musl/2023/03/20/1
As a workaround, on this platform, vasnwprintf needs to - avoid %n, - do the padding itself, instead of leaving it to the swprintf primitive. 2023-03-19 Bruno Haible <br...@clisp.org> vasnwprintf: Fix test failures on musl libc. * m4/vasnprintf.m4 (gl_PREREQ_VASNWPRINTF): Invoke gl_MUSL_LIBC. * lib/vasnprintf.c (VASNPRINTF): On musl libc, when WIDE_CHAR_VERSION, - force pad_ourselves to be 1, - don't use %n. Fix zero-padding when the result starts with a prefix "0x" or "0b". * modules/vasnwprintf (Files): Add musl.m4. * doc/posix-functions/swprintf.texi: Mention two musl libc bugs. diff --git a/doc/posix-functions/swprintf.texi b/doc/posix-functions/swprintf.texi index 906ee521aa..a87bc06c86 100644 --- a/doc/posix-functions/swprintf.texi +++ b/doc/posix-functions/swprintf.texi @@ -26,11 +26,21 @@ glibc when used with @code{_FORTIFY_SOURCE >= 2} (set by default on Ubuntu), macOS 11.1, MSVC 14. @item +This function sometimes returns a wrong value through the @samp{n} directive +on some platforms: +@c https://www.openwall.com/lists/musl/2023/03/19/1 +musl libc 1.2.3. +@item On Windows and 32-bit AIX platforms, @code{wchar_t} is a 16-bit type and therefore cannot accommodate all Unicode characters. @item On Windows, this function does not take a buffer size as second argument. @item +This function ignores the minimum field width in the @samp{lc} directive +on some platforms: +@c https://www.openwall.com/lists/musl/2023/03/20/1 +musl libc 1.2.3. +@item This function does not support the @samp{b} directive, required by ISO C23, on some platforms: glibc 2.34, musl libc, macOS 12.5, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index c2f10cb0c4..50ff2e64dd 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -4932,7 +4932,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, { arg_type type = a.arg[dp->arg_index].type; int flags = dp->flags; -#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION int has_width; #endif #if !USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION @@ -4947,7 +4947,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, #else # define prec_ourselves 0 #endif -#if NEED_PRINTF_FLAG_LEFTADJUST +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST # define pad_ourselves 1 #elif !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION int pad_ourselves; @@ -4964,7 +4964,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, TCHAR_T *tmp; #endif -#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION has_width = 0; #endif #if !USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION @@ -4995,7 +4995,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, width = xsum (xtimes (width, 10), *digitp++ - '0'); while (digitp != dp->width_end); } -#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION has_width = 1; #endif } @@ -5050,7 +5050,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, #endif /* Decide whether to perform the padding ourselves. */ -#if !NEED_PRINTF_FLAG_LEFTADJUST && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION) +#if !((WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST) && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION) switch (dp->conversion) { # if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO @@ -5210,7 +5210,8 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, || (defined __APPLE__ && defined __MACH__) \ || defined __OpenBSD__ \ || defined __ANDROID__ \ - || (defined _WIN32 && ! defined __CYGWIN__)) + || (defined _WIN32 && ! defined __CYGWIN__)) \ + || (WIDE_CHAR_VERSION && MUSL_LIBC) /* We can avoid passing %n and instead rely on SNPRINTF's return value if - !WIDE_CHAR_VERSION, because if WIDE_CHAR_VERSION, @@ -5263,6 +5264,9 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, - Although the gl_SNPRINTF_RETVAL_C99 test fails, snprintf allows us to recognize the case of an insufficient buffer size: it returns -1 in this case. */ + /* Additionally, in the WIDE_CHAR_VERSION case, we cannot use %n + on musl libc because we would run into an swprintf() bug. + See <https://www.openwall.com/lists/musl/2023/03/19/1>. */ fbp[1] = '\0'; # else /* AIX <= 5.1, HP-UX, IRIX, OSF/1, Solaris <= 9, BeOS */ fbp[1] = '%'; @@ -5768,7 +5772,7 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, /* Here count <= allocated - length. */ /* Perform padding. */ -#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION if (pad_ourselves && has_width) { size_t w; @@ -5827,6 +5831,22 @@ VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, if ((*pad_ptr >= 'A' && *pad_ptr <= 'Z') || (*pad_ptr >= 'a' && *pad_ptr <= 'z')) pad_ptr = NULL; + else + /* Do the zero-padding after the "0x" or + "0b" prefix, not before. */ + if (p - rp >= 2 + && *rp == '0' + && (((dp->conversion == 'a' + || dp->conversion == 'x') + && rp[1] == 'x') + || ((dp->conversion == 'A' + || dp->conversion == 'X') + && rp[1] == 'X') + || (dp->conversion == 'b' + && rp[1] == 'b') + || (dp->conversion == 'B' + && rp[1] == 'B'))) + pad_ptr += 2; } /* The generated string now extends from rp to p, with the zero padding insertion point being at diff --git a/m4/vasnprintf.m4 b/m4/vasnprintf.m4 index b868aed08b..8e582dd791 100644 --- a/m4/vasnprintf.m4 +++ b/m4/vasnprintf.m4 @@ -1,4 +1,4 @@ -# vasnprintf.m4 serial 41 +# vasnprintf.m4 serial 42 dnl Copyright (C) 2002-2004, 2006-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -96,6 +96,7 @@ AC_DEFUN_ONCE([gl_PREREQ_VASNWPRINTF] [ AC_CHECK_FUNCS([wcsnlen mbrtowc]) AC_CHECK_DECLS([_snwprintf], , , [[#include <stdio.h>]]) + gl_MUSL_LIBC gl_PREREQ_VASNXPRINTF ]) diff --git a/modules/vasnwprintf b/modules/vasnwprintf index 6c7d63a5f6..07a078696c 100644 --- a/modules/vasnwprintf +++ b/modules/vasnwprintf @@ -21,6 +21,7 @@ m4/vasnprintf.m4 m4/printf.m4 m4/math_h.m4 m4/exponentd.m4 +m4/musl.m4 Depends-on: stdio