mirror of
https://sourceware.org/git/glibc.git
synced 2025-12-20 01:12:17 +08:00
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 commit is contained in:
@@ -260,6 +260,7 @@ tests := \
|
||||
tllformat \
|
||||
tst-bz11319 \
|
||||
tst-bz11319-fortify2 \
|
||||
tst-bz12701-c \
|
||||
tst-cookie \
|
||||
tst-dprintf-length \
|
||||
tst-fclose-devzero \
|
||||
|
||||
175
stdio-common/tst-bz12701-c.c
Normal file
175
stdio-common/tst-bz12701-c.c
Normal file
@@ -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
|
||||
<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 = 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 <support/test-driver.c>
|
||||
@@ -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:
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user