[PATCH v2 3/3] stdio-common: Allow partially-filled %mc buffers [BZ #12701]
Carlos O'Donell
carlos@redhat.com
Mon Jun 1 17:00:01 GMT 2026
On 5/27/26 12:57 PM, DJ Delorie wrote:
> This is a backwards-compatible alternative to the main solution to
> the %mc part of 12701. The allocated buffer is expanded to the
> requested size and NUL padded, but truncated reads are allowed.
LGTM. While there is some dead code in vfscanf-internal.c given that
you do the filling, I don't think removing it is a good diea. The
point is to make this a minimal patch without changing other stuff
too (and it's harder to prove and we discussed this aspect).
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
> ---
> localedata/Makefile | 1 +
> localedata/tst-bz12701-lc2.c | 47 +++++++++++++++++++++++++++++++++
> stdio-common/Makefile | 1 +
> stdio-common/tst-bz12701-c2.c | 46 ++++++++++++++++++++++++++++++++
> stdio-common/vfscanf-internal.c | 16 ++++++++---
> 5 files changed, 108 insertions(+), 3 deletions(-)
> create mode 100644 localedata/tst-bz12701-lc2.c
> create mode 100644 stdio-common/tst-bz12701-c2.c
>
> diff --git a/localedata/Makefile b/localedata/Makefile
> index bff5c0bc71..e212facef0 100644
> --- a/localedata/Makefile
> +++ b/localedata/Makefile
> @@ -237,6 +237,7 @@ tests = \
> bug-setlocale1 \
> bug-usesetlocale \
> tst-bz12701-lc \
> + tst-bz12701-lc2 \
> tst-bz13988 \
> tst-c-utf8-consistency \
> tst-digits \
> diff --git a/localedata/tst-bz12701-lc2.c b/localedata/tst-bz12701-lc2.c
> new file mode 100644
> index 0000000000..b24e86df0b
> --- /dev/null
> +++ b/localedata/tst-bz12701-lc2.c
> @@ -0,0 +1,47 @@
> +/* Verify scanf memory handling with the 'c' conversion (BZ #12701).
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <malloc.h>
> +#include <string.h>
> +
> +#include <libc-diag.h>
> +#include <support/check.h>
> +#include <support/next_to_fault.h>
> +#include <support/xstdio.h>
> +
> +static int
> +do_test (void)
> +{
> + wchar_t *c = NULL;
> + int i;
> +
> + TEST_VERIFY (sscanf ("1234", "%30mlc", &c) == 1);
> +
> + TEST_VERIFY (c != NULL);
> + TEST_COMPARE_BLOB (c, 5 * sizeof (wchar_t),
OK. 5 * sizeof (wchar_t) is correct now.
> + L"1234\0", 5 * sizeof (wchar_t));
> + for (i = 5; i < 30; i ++)
> + TEST_VERIFY (c[i] == L'\0');
> +
> + TEST_VERIFY (malloc_usable_size (c) >= 30 * sizeof(wchar_t));
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index fdb545242e..27e7ea20f0 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -261,6 +261,7 @@ tests := \
> tst-bz11319 \
> tst-bz11319-fortify2 \
> tst-bz12701-c \
> + tst-bz12701-c2 \
> tst-cookie \
> tst-dprintf-length \
> tst-fclose-devzero \
> diff --git a/stdio-common/tst-bz12701-c2.c b/stdio-common/tst-bz12701-c2.c
> new file mode 100644
> index 0000000000..5f9ca7c592
> --- /dev/null
> +++ b/stdio-common/tst-bz12701-c2.c
> @@ -0,0 +1,46 @@
> +/* Verify scanf memory handling with the 'c' conversion (BZ #12701).
> + Copyright (C) 2026 Free Software Foundation, Inc.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include <stdio.h>
> +#include <malloc.h>
> +#include <string.h>
> +
> +#include <libc-diag.h>
> +#include <support/check.h>
> +#include <support/next_to_fault.h>
> +#include <support/xstdio.h>
> +
> +static int
> +do_test (void)
> +{
> + char *c = NULL;
> + int i;
> +
> + TEST_VERIFY (sscanf ("1234", "%30mc", &c) == 1);
> +
> + TEST_VERIFY (c != NULL);
> + TEST_COMPARE_BLOB (c, 5, "1234\0", 5);
OK. char is the same as bytes.
> + for (i = 5; i < 30; i ++)
> + TEST_VERIFY (c[i] == '\0');
> +
> + TEST_VERIFY (malloc_usable_size (c) >= 30);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c
> index 17b5565d0f..90a1886951 100644
> --- a/stdio-common/vfscanf-internal.c
> +++ b/stdio-common/vfscanf-internal.c
> @@ -780,9 +780,9 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> conv_error (); \
> } while (0)
> #ifdef COMPILE_WSCANF
> - STRING_ARG (str, char, 100);
> + STRING_ARG (str, char, (width > 0 ? width : 1));
> #else
> - STRING_ARG (str, char, (width > 1024 ? 1024 : width));
> + STRING_ARG (str, char, (width > 0 ? width : 1));
OK.
> #endif
>
> c = inchar ();
> @@ -891,6 +891,11 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
>
> if (!(flags & SUPPRESS))
> {
> + /* If the buffer isn't completely filled, pad it with NULs. */
> + if (flags & MALLOC)
> + while (width-- > 0)
> + *str++ = '\0';
> +
OK.
> if ((flags & MALLOC) && str - *strptr != strsize)
> {
> char *cp = (char *) realloc (*strptr, str - *strptr);
> @@ -908,7 +913,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> if (width == -1)
> width = 1;
>
> - STRING_ARG (wstr, wchar_t, (width > 1024 ? 1024 : width));
> + STRING_ARG (wstr, wchar_t, (width > 0 ? width : 1));
OK.
>
> c = inchar ();
> if (__glibc_unlikely (c == EOF))
> @@ -1044,6 +1049,11 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
>
> if (!(flags & SUPPRESS))
> {
> + /* If the buffer isn't completely filled, pad it with NULs. */
> + if (flags & MALLOC)
> + while (width-- > 0)
> + *wstr++ = L'\0';
> +
OK.
> if ((flags & MALLOC) && wstr - (wchar_t *) *strptr != strsize)
> {
> wchar_t *cp = (wchar_t *) realloc (*strptr,
--
Cheers,
Carlos.
More information about the Libc-stable
mailing list