458 lines
14 KiB
Diff
458 lines
14 KiB
Diff
commit 97926e9017f3faeaacce9337f1288460f5e6ec7d
|
|
Author: Maciej W. Rozycki <macro@redhat.com>
|
|
Date: Wed May 27 12:57:10 2026 -0400
|
|
|
|
stdio-common: Reject insufficient character data in scanf [BZ #12701]
|
|
|
|
Reject invalid formatted scanf character data with the 'c' conversion
|
|
where there is not enough input available to satisfy the field width
|
|
requested. It is required by ISO C that this conversion matches a
|
|
sequence of characters of exactly the number specified by the field
|
|
width and it is also already documented as such in our own manual:
|
|
|
|
"It reads precisely the next N characters, and fails if it cannot get
|
|
that many."
|
|
|
|
Currently a matching success is instead incorrectly produced where the
|
|
EOF condition is encountered before the required number of characters
|
|
has been retrieved, and the characters actually obtained are stored in
|
|
the buffer provided.
|
|
|
|
Add test cases accordingly and remove placeholders from 'c' conversion
|
|
input data for the existing scanf tests.
|
|
|
|
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
|
|
|
[This is a modified version of commit 2b16c76609, which tests for the
|
|
old behavior and only includes the test cases, for older branches
|
|
and downstream backports - DJ]
|
|
|
|
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
|
|
|
|
Conflicts:
|
|
localedata/Makefile
|
|
stdio-common/Makefile
|
|
(usual test differences)
|
|
|
|
diff -Nrup a/localedata/Makefile b/localedata/Makefile
|
|
--- a/localedata/Makefile 2026-06-16 09:15:19.295653341 -0400
|
|
+++ b/localedata/Makefile 2026-06-16 09:11:51.111414458 -0400
|
|
@@ -160,6 +160,7 @@ tests = \
|
|
bug-iconv-trans \
|
|
bug-setlocale1 \
|
|
bug-usesetlocale \
|
|
+ tst-bz12701-lc \
|
|
tst-c-utf8-consistency \
|
|
tst-digits \
|
|
tst-iconv-math-trans \
|
|
diff --git a/localedata/tst-bz12701-lc.c b/localedata/tst-bz12701-lc.c
|
|
new file mode 100644
|
|
index 0000000000..23c2ab7d2a
|
|
--- /dev/null
|
|
+++ b/localedata/tst-bz12701-lc.c
|
|
@@ -0,0 +1,218 @@
|
|
+/* Verify scanf field width handling with the 'lc' conversion (BZ #12701).
|
|
+ Copyright (C) 2025-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 <locale.h>
|
|
+#include <stddef.h>
|
|
+#include <stdio.h>
|
|
+#include <string.h>
|
|
+#include <wchar.h>
|
|
+
|
|
+#include <libc-diag.h>
|
|
+#include <support/check.h>
|
|
+#include <support/next_to_fault.h>
|
|
+#include <support/xstdio.h>
|
|
+
|
|
+/* 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 = xfmemopen (b, sizeof (s), "r");
|
|
+
|
|
+ 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_COMPARE (fscanf (f, "%64lc%n", c, &n), 1);
|
|
+ TEST_COMPARE (n , 49);
|
|
+ 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 = xfmemopen (b, 3, "r");
|
|
+
|
|
+ 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 = xfmemopen (b, 3, "r");
|
|
+
|
|
+ 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) == 1);
|
|
+ TEST_VERIFY_EXIT (n == 2);
|
|
+ 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 <support/test-driver.c>
|
|
diff -Nrup a/stdio-common/Makefile b/stdio-common/Makefile
|
|
--- a/stdio-common/Makefile 2026-06-16 09:15:19.320447732 -0400
|
|
+++ b/stdio-common/Makefile 2026-06-16 09:14:09.814624522 -0400
|
|
@@ -217,6 +217,7 @@ tests := \
|
|
tllformat \
|
|
tst-bz11319 \
|
|
tst-bz11319-fortify2 \
|
|
+ tst-bz12701-c \
|
|
tst-cookie \
|
|
tst-fclose-devzero \
|
|
tst-fclose-offset \
|
|
diff --git a/stdio-common/tst-bz12701-c.c b/stdio-common/tst-bz12701-c.c
|
|
new file mode 100644
|
|
index 0000000000..4f3616fbfd
|
|
--- /dev/null
|
|
+++ b/stdio-common/tst-bz12701-c.c
|
|
@@ -0,0 +1,169 @@
|
|
+/* Verify scanf field width handling with the 'c' conversion (BZ #12701).
|
|
+ Copyright (C) 2025-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 <string.h>
|
|
+
|
|
+#include <libc-diag.h>
|
|
+#include <support/check.h>
|
|
+#include <support/next_to_fault.h>
|
|
+#include <support/xstdio.h>
|
|
+
|
|
+/* 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 = xfmemopen (b, sizeof (s), "r");
|
|
+
|
|
+ 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) == 1);
|
|
+ TEST_VERIFY_EXIT (n == 10);
|
|
+ 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 = xfmemopen (b, 3, "r");
|
|
+
|
|
+ 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 = xfmemopen (b, 3, "r");
|
|
+
|
|
+ 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) == 1);
|
|
+ TEST_VERIFY_EXIT (n == 1);
|
|
+ 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 <support/test-driver.c>
|