diff --git a/localedata/Makefile b/localedata/Makefile index 5d2ebd0256..01b32348d4 100644 --- a/localedata/Makefile +++ b/localedata/Makefile @@ -236,6 +236,7 @@ tests = \ bug-iconv-trans \ bug-setlocale1 \ bug-usesetlocale \ + tst-bz12701-lc \ tst-bz13988 \ tst-c-utf8-consistency \ tst-digits \ diff --git a/localedata/tst-bz12701-lc.c b/localedata/tst-bz12701-lc.c new file mode 100644 index 0000000000..fcc9f29a14 --- /dev/null +++ b/localedata/tst-bz12701-lc.c @@ -0,0 +1,224 @@ +/* Verify scanf field width handling with the 'lc' conversion (BZ #12701). + Copyright (C) 2025 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 + . */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* Compare character-wise the initial part of the wide character object + pointed to by WS corresponding to wide characters obtained by the + conversion of first N bytes of the multibyte character object pointed + to by S. */ + +static int +tst_bz12701_lc_memcmp (const wchar_t *ds, const char *s, size_t n) +{ + size_t nc = mbsnrtowcs (NULL, &s, n, 0, NULL); + + struct support_next_to_fault ntf; + ntf = support_next_to_fault_allocate (nc * sizeof (wchar_t)); + wchar_t *ss = (wchar_t *) ntf.buffer; + + mbsnrtowcs (ss, &s, n, nc, NULL); + int r = wmemcmp (ds, ss, nc); + + support_next_to_fault_free (&ntf); + + return r; +} + +/* Verify various aspects of field width handling, including the data + obtained, the number of bytes consumed, and the stream position. */ + +static int +do_test (void) +{ + if (setlocale (LC_ALL, "pl_PL.UTF-8") == NULL) + FAIL_EXIT1 ("setlocale (LC_ALL, \"pl_PL.UTF-8\")"); + + /* Part of a tongue-twister in Polish, which says: + "On a rainy morning cuckoos and warblers, rather than starting + on earthworms, stuffed themselves fasted with the flesh of cress." */ + static const char s[126] = "Dżdżystym rankiem gżegżółki i piegże, " + "zamiast wziąć się za dżdżownice, " + "nażarły się na czczo miąższu rzeżuchy"; + + const char *sp = s; + size_t nc; + TEST_VERIFY_EXIT ((nc = mbsnrtowcs (NULL, &sp, sizeof (s), 0, NULL)) == 108); + + struct support_next_to_fault ntfo, ntfi; + ntfo = support_next_to_fault_allocate (nc * sizeof (wchar_t)); + ntfi = support_next_to_fault_allocate (sizeof (s)); + wchar_t *e = (wchar_t *) ntfo.buffer + nc; + char *b = ntfi.buffer; + + wchar_t *c; + FILE *f; + int ic; + int n; + int i; + + memcpy (ntfi.buffer, s, sizeof (s)); + + ic = i = 0; + f = fmemopen (b, sizeof (s), "r"); + if (f == NULL) + FAIL_EXIT1 ("fmemopen: %m"); + + c = e - 1; + TEST_VERIFY_EXIT (ftell (f) == i); + /* Avoid: "warning: zero width in gnu_scanf format [-Werror=format=]". */ + DIAG_PUSH_NEEDS_COMMENT; + DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat"); + TEST_VERIFY_EXIT (fscanf (f, "%0lc%n", c, &n) == 1); + DIAG_POP_NEEDS_COMMENT; + TEST_VERIFY_EXIT (n == 1); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); + ic += 1; + i += n; + + c = e - 1; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%lc%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 2); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); + ic += 1; + i += n; + + c = e - 1; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%1lc%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 1); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); + ic += 1; + i += n; + + c = e - 2; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 3); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); + ic += 2; + i += n; + + c = e - 4; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%4lc%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 4); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); + ic += 4; + i += n; + + c = e - 8; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%8lc%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 8); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); + ic += 8; + i += n; + + c = e - 16; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%16lc%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 20); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); + ic += 16; + i += n; + + c = e - 32; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%32lc%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 38); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); + ic += 32; + i += n; + + c = e - (nc - ic); + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%64lc%n", c, &n) == EOF); + TEST_VERIFY_EXIT (n == 38); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, sizeof (s) - i) == 0); + + TEST_VERIFY_EXIT (ftell (f) == sizeof (s)); + TEST_VERIFY_EXIT (feof (f) != 0); + + xfclose (f); + + ic = i = 0; + f = fmemopen (b, 3, "r"); + if (f == NULL) + FAIL_EXIT1 ("fmemopen: %m"); + + c = e - 2; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 3); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); + ic += 2; + i += n; + + c = e - (nc - ic); + TEST_VERIFY_EXIT (feof (f) == 0); + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == EOF); + TEST_VERIFY_EXIT (n == 3); + + TEST_VERIFY_EXIT (ftell (f) == 3); + TEST_VERIFY_EXIT (feof (f) != 0); + + xfclose (f); + + ic = i = 0; + f = fmemopen (b, 3, "r"); + if (f == NULL) + FAIL_EXIT1 ("fmemopen: %m"); + + c = e - 1; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%lc%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 1); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, n) == 0); + ic += 1; + i += n; + + c = e - (nc - ic); + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%2lc%n", c, &n) == EOF); + TEST_VERIFY_EXIT (n == 1); + TEST_VERIFY_EXIT (tst_bz12701_lc_memcmp (c, s + i, 3 - i) == 0); + + TEST_VERIFY_EXIT (ftell (f) == 3); + TEST_VERIFY_EXIT (feof (f) != 0); + + xfclose (f); + + support_next_to_fault_free (&ntfi); + support_next_to_fault_free (&ntfo); + + return 0; +} + +#include diff --git a/stdio-common/Makefile b/stdio-common/Makefile index ae2b90c2ad..8da164695f 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -260,6 +260,7 @@ tests := \ tllformat \ tst-bz11319 \ tst-bz11319-fortify2 \ + tst-bz12701-c \ tst-cookie \ tst-dprintf-length \ tst-fclose-devzero \ diff --git a/stdio-common/tst-bz12701-c.c b/stdio-common/tst-bz12701-c.c new file mode 100644 index 0000000000..ffb4330bfb --- /dev/null +++ b/stdio-common/tst-bz12701-c.c @@ -0,0 +1,175 @@ +/* Verify scanf field width handling with the 'c' conversion (BZ #12701). + Copyright (C) 2025 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 + . */ + +#include +#include + +#include +#include +#include +#include + +/* Verify various aspects of field width handling, including the data + obtained, the number of bytes consumed, and the stream position. */ + +static int +do_test (void) +{ + static const char s[43] = "The quick brown fox jumps over the lazy dog"; + struct support_next_to_fault ntfo, ntfi; + ntfo = support_next_to_fault_allocate (sizeof (s)); + ntfi = support_next_to_fault_allocate (sizeof (s)); + char *e = ntfo.buffer + sizeof (s); + char *b = ntfi.buffer; + + char *c; + FILE *f; + int n; + int i; + + memcpy (ntfi.buffer, s, sizeof (s)); + + i = 0; + f = fmemopen (b, sizeof (s), "r"); + if (f == NULL) + FAIL_EXIT1 ("fmemopen: %m"); + + c = e - 1; + TEST_VERIFY_EXIT (ftell (f) == i); + /* Avoid: "warning: zero width in gnu_scanf format [-Werror=format=]". */ + DIAG_PUSH_NEEDS_COMMENT; + DIAG_IGNORE_NEEDS_COMMENT (4.9, "-Wformat"); + TEST_VERIFY_EXIT (fscanf (f, "%0c%n", c, &n) == 1); + DIAG_POP_NEEDS_COMMENT; + TEST_VERIFY_EXIT (n == 1); + TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); + i += n; + + c = e - 1; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%c%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 1); + TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); + i += n; + + c = e - 1; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%1c%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 1); + TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); + i += n; + + c = e - 2; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 2); + TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); + i += n; + + c = e - 4; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%4c%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 4); + TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); + i += n; + + c = e - 8; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%8c%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 8); + TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); + i += n; + + c = e - 16; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%16c%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 16); + TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); + i += n; + + c = e - (sizeof (s) - i); + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%32c%n", c, &n) == EOF); + TEST_VERIFY_EXIT (n == 16); + TEST_VERIFY_EXIT (memcmp (c, s + i, sizeof (s) - i) == 0); + + TEST_VERIFY_EXIT (ftell (f) == sizeof (s)); + TEST_VERIFY_EXIT (feof (f) != 0); + + xfclose (f); + + i = 0; + f = fmemopen (b, 3, "r"); + if (f == NULL) + FAIL_EXIT1 ("fmemopen: %m"); + + c = e - 1; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%c%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 1); + TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); + i += n; + + c = e - 2; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 2); + TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); + i += n; + + c = e - (3 - i); + TEST_VERIFY_EXIT (feof (f) == 0); + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == EOF); + TEST_VERIFY_EXIT (n == 2); + + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (feof (f) != 0); + + xfclose (f); + + i = 0; + f = fmemopen (b, 3, "r"); + if (f == NULL) + FAIL_EXIT1 ("fmemopen: %m"); + + c = e - 2; + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == 1); + TEST_VERIFY_EXIT (n == 2); + TEST_VERIFY_EXIT (memcmp (c, s + i, n) == 0); + i += n; + + c = e - (3 - i); + TEST_VERIFY_EXIT (ftell (f) == i); + TEST_VERIFY_EXIT (fscanf (f, "%2c%n", c, &n) == EOF); + TEST_VERIFY_EXIT (n == 2); + TEST_VERIFY_EXIT (memcmp (c, s + i, 3 - i) == 0); + + TEST_VERIFY_EXIT (ftell (f) == 3); + TEST_VERIFY_EXIT (feof (f) != 0); + + xfclose (f); + + support_next_to_fault_free (&ntfi); + support_next_to_fault_free (&ntfo); + + return 0; +} + +#include diff --git a/stdio-common/tst-scanf-format-c-c.input b/stdio-common/tst-scanf-format-c-c.input index a3a6ee26af..67c78651ba 100644 --- a/stdio-common/tst-scanf-format-c-c.input +++ b/stdio-common/tst-scanf-format-c-c.input @@ -22,30 +22,14 @@ %*2c:brown fox:0:2: %2c:jumps over the lazy dog:1:2:ju: %*2c:jumps over the lazy dog:0:2: -# BZ12701 %5c:The:0:-1: -# BZ12701 %*5c:The:0:-1: %5c:quick:1:5:quick: %*5c:quick:0:5: %5c:brown fox:1:5:brown: %*5c:brown fox:0:5: %5c:jumps over the lazy dog:1:5:jumps: %*5c:jumps over the lazy dog:0:5: -# BZ12701 %10c:The:0:-1: -# BZ12701 %*10c:The:0:-1: -# BZ12701 %10c:quick:0:-1: -# BZ12701 %*10c:quick:0:-1: -# BZ12701 %10c:brown fox:0:-1: -# BZ12701 %*10c:brown fox:0:-1: %10c:jumps over the lazy dog:1:10:jumps over: %*10c:jumps over the lazy dog:0:10: -# BZ12701 %25c:The:0:-1: -# BZ12701 %*25c:The:0:-1: -# BZ12701 %25c:quick:0:-1: -# BZ12701 %*25c:quick:0:-1: -# BZ12701 %25c:brown fox:0:-1: -# BZ12701 %*25c:brown fox:0:-1: -# BZ12701 %25c:jumps over the lazy dog:0:-1: -# BZ12701 %*25c:jumps over the lazy dog:0:-1: %5c: The :1:5: The : %*5c: The :0:5: %5c: quick :1:5: quic: @@ -54,11 +38,5 @@ %*5c: brown fox :0:5: %5c: jumps over the lazy dog :1:5: jump: %*5c: jumps over the lazy dog :0:5: -# BZ12701 %25c: The :0:-1: -# BZ12701 %*25c: The :0:-1: -# BZ12701 %25c: quick :0:-1: -# BZ12701 %*25c: quick :0:-1: -# BZ12701 %25c: brown fox :0:-1: -# BZ12701 %*25c: brown fox :0:-1: %25c: jumps over the lazy dog :1:25: jumps over the lazy dog : %*25c: jumps over the lazy dog :0:25: diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c index daeb068dda..d8facb6e89 100644 --- a/stdio-common/vfscanf-internal.c +++ b/stdio-common/vfscanf-internal.c @@ -898,6 +898,8 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr, else while (--width > 0 && inchar () != EOF); #endif + if (width > 0) + input_error (); if (!(flags & SUPPRESS)) { @@ -1051,6 +1053,8 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr, while (--width > 0 && inchar () != EOF); } #endif + if (width > 0) + input_error (); if (!(flags & SUPPRESS)) {