diff options
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | stdio-common/Makefile | 1 | ||||
-rw-r--r-- | stdio-common/bug22.c | 2 | ||||
-rw-r--r-- | stdio-common/tst-vfprintf-width-prec-alloc.c | 41 | ||||
-rw-r--r-- | stdio-common/vfprintf.c | 120 |
5 files changed, 47 insertions, 120 deletions
diff --git a/NEWS b/NEWS index 1e289bb7a2..7993b76ff2 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,7 @@ Deprecated and removed features, and other changes affecting compatibility: The following bugs are resolved with this release: + [14231] stdio-common tests memory requirements [18035] Fix pldd hang [19444] build failures with -O1 due to -Wmaybe-uninitialized [20018] getaddrinfo should reject IP addresses with trailing characters @@ -78,6 +79,7 @@ The following bugs are resolved with this release: [25423] Array overflow in backtrace on powerpc [25933] Off by one error in __strncmp_avx2 [25691] stdio: Remove memory leak from multibyte conversion + [26211] printf integer overflow calculating allocation size [27130] "rep movsb" performance issue [27177] GLIBC_TUNABLES=glibc.cpu.x86_ibt=on:glibc.cpu.x86_shstk=on doesn't work [27457] vzeroupper use in AVX2 multiarch string functions cause HTM aborts @@ -85,6 +87,7 @@ The following bugs are resolved with this release: [28755] overflow bug in wcsncmp_avx2 and wcsncmp_evex [28896] strncmp-avx2-rtm and wcsncmp-avx2-rtm fallback on non-rtm variants when avoiding overflow + [29530] segfault in printf handling thousands separator Security related changes: diff --git a/stdio-common/Makefile b/stdio-common/Makefile index 51062a7dbf..d76b47bd5f 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -64,6 +64,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \ tst-scanf-round \ tst-renameat2 \ tst-printf-bz25691 \ + tst-vfprintf-width-prec-alloc test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble diff --git a/stdio-common/bug22.c b/stdio-common/bug22.c index b26399acb7..e12b01731e 100644 --- a/stdio-common/bug22.c +++ b/stdio-common/bug22.c @@ -42,7 +42,7 @@ do_test (void) ret = fprintf (fp, "%." SN3 "d", 1); printf ("ret = %d\n", ret); - if (ret != -1 || errno != EOVERFLOW) + if (ret != N3) return 1; ret = fprintf (fp, "%" SN2 "d%" SN2 "d", 1, 1); diff --git a/stdio-common/tst-vfprintf-width-prec-alloc.c b/stdio-common/tst-vfprintf-width-prec-alloc.c new file mode 100644 index 0000000000..0a74b53a33 --- /dev/null +++ b/stdio-common/tst-vfprintf-width-prec-alloc.c @@ -0,0 +1,41 @@ +/* Test large width or precision does not involve large allocation. + Copyright (C) 2020 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 <sys/resource.h> +#include <support/check.h> + +char test_string[] = "test"; + +static int +do_test (void) +{ + struct rlimit limit; + TEST_VERIFY_EXIT (getrlimit (RLIMIT_AS, &limit) == 0); + limit.rlim_cur = 200 * 1024 * 1024; + TEST_VERIFY_EXIT (setrlimit (RLIMIT_AS, &limit) == 0); + FILE *fp = fopen ("/dev/null", "w"); + TEST_VERIFY_EXIT (fp != NULL); + TEST_COMPARE (fprintf (fp, "%1000000000d", 1), 1000000000); + TEST_COMPARE (fprintf (fp, "%.1000000000s", test_string), 4); + TEST_COMPARE (fprintf (fp, "%1000000000d %1000000000d", 1, 2), 2000000001); + TEST_COMPARE (fprintf (fp, "%2$.*1$s", 0x7fffffff, test_string), 4); + return 0; +} + +#include <support/test-driver.c> diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c index dab56b6ba2..6b83ba91a1 100644 --- a/stdio-common/vfprintf.c +++ b/stdio-common/vfprintf.c @@ -42,10 +42,6 @@ #include <libioP.h> -/* In some cases we need extra space for all the output which is not - counted in the width of the string. We assume 32 characters is - enough. */ -#define EXTSIZ 32 #define ARGCHECK(S, Format) \ do \ { \ @@ -1295,7 +1291,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) /* Buffer intermediate results. */ CHAR_T work_buffer[WORK_BUFFER_SIZE]; - CHAR_T *workstart = NULL; CHAR_T *workend; /* We have to save the original argument pointer. */ @@ -1404,7 +1399,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) UCHAR_T pad = L_(' ');/* Padding character. */ CHAR_T spec; - workstart = NULL; workend = work_buffer + WORK_BUFFER_SIZE; /* Get current character in format string. */ @@ -1496,31 +1490,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) pad = L_(' '); left = 1; } - - if (__glibc_unlikely (width >= INT_MAX / sizeof (CHAR_T) - EXTSIZ)) - { - __set_errno (EOVERFLOW); - done = -1; - goto all_done; - } - - if (width >= WORK_BUFFER_SIZE - EXTSIZ) - { - /* We have to use a special buffer. */ - size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T); - if (__libc_use_alloca (needed)) - workend = (CHAR_T *) alloca (needed) + width + EXTSIZ; - else - { - workstart = (CHAR_T *) malloc (needed); - if (workstart == NULL) - { - done = -1; - goto all_done; - } - workend = workstart + width + EXTSIZ; - } - } } JUMP (*f, step1_jumps); @@ -1528,31 +1497,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) LABEL (width): width = read_int (&f); - if (__glibc_unlikely (width == -1 - || width >= INT_MAX / sizeof (CHAR_T) - EXTSIZ)) + if (__glibc_unlikely (width == -1)) { __set_errno (EOVERFLOW); done = -1; goto all_done; } - if (width >= WORK_BUFFER_SIZE - EXTSIZ) - { - /* We have to use a special buffer. */ - size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T); - if (__libc_use_alloca (needed)) - workend = (CHAR_T *) alloca (needed) + width + EXTSIZ; - else - { - workstart = (CHAR_T *) malloc (needed); - if (workstart == NULL) - { - done = -1; - goto all_done; - } - workend = workstart + width + EXTSIZ; - } - } if (*f == L_('$')) /* Oh, oh. The argument comes from a positional parameter. */ goto do_positional; @@ -1601,34 +1552,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) } else prec = 0; - if (prec > width && prec > WORK_BUFFER_SIZE - EXTSIZ) - { - /* Deallocate any previously allocated buffer because it is - too small. */ - if (__glibc_unlikely (workstart != NULL)) - free (workstart); - workstart = NULL; - if (__glibc_unlikely (prec >= INT_MAX / sizeof (CHAR_T) - EXTSIZ)) - { - __set_errno (EOVERFLOW); - done = -1; - goto all_done; - } - size_t needed = ((size_t) prec + EXTSIZ) * sizeof (CHAR_T); - - if (__libc_use_alloca (needed)) - workend = (CHAR_T *) alloca (needed) + prec + EXTSIZ; - else - { - workstart = (CHAR_T *) malloc (needed); - if (workstart == NULL) - { - done = -1; - goto all_done; - } - workend = workstart + prec + EXTSIZ; - } - } JUMP (*f, step2_jumps); /* Process 'h' modifier. There might another 'h' following. */ @@ -1692,10 +1615,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) /* The format is correctly handled. */ ++nspecs_done; - if (__glibc_unlikely (workstart != NULL)) - free (workstart); - workstart = NULL; - /* Look for next format specifier. */ #ifdef COMPILE_WPRINTF f = __find_specwc ((end_of_spec = ++f)); @@ -1713,18 +1632,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) /* Hand off processing for positional parameters. */ do_positional: - if (__glibc_unlikely (workstart != NULL)) - { - free (workstart); - workstart = NULL; - } done = printf_positional (s, format, readonly_format, ap, &ap_save, done, nspecs_done, lead_str_end, work_buffer, save_errno, grouping, thousands_sep); all_done: - if (__glibc_unlikely (workstart != NULL)) - free (workstart); /* Unlock the stream. */ _IO_funlockfile (s); _IO_cleanup_region_end (0); @@ -1767,8 +1679,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format, /* Just a counter. */ size_t cnt; - CHAR_T *workstart = NULL; - if (grouping == (const char *) -1) { #ifdef COMPILE_WPRINTF @@ -1957,7 +1867,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format, char pad = specs[nspecs_done].info.pad; CHAR_T spec = specs[nspecs_done].info.spec; - workstart = NULL; CHAR_T *workend = work_buffer + WORK_BUFFER_SIZE; /* Fill in last information. */ @@ -1991,27 +1900,6 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format, prec = specs[nspecs_done].info.prec; } - /* Maybe the buffer is too small. */ - if (MAX (prec, width) + EXTSIZ > WORK_BUFFER_SIZE) - { - if (__libc_use_alloca ((MAX (prec, width) + EXTSIZ) - * sizeof (CHAR_T))) - workend = ((CHAR_T *) alloca ((MAX (prec, width) + EXTSIZ) - * sizeof (CHAR_T)) - + (MAX (prec, width) + EXTSIZ)); - else - { - workstart = (CHAR_T *) malloc ((MAX (prec, width) + EXTSIZ) - * sizeof (CHAR_T)); - if (workstart == NULL) - { - done = -1; - goto all_done; - } - workend = workstart + (MAX (prec, width) + EXTSIZ); - } - } - /* Process format specifiers. */ while (1) { @@ -2085,18 +1973,12 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format, break; } - if (__glibc_unlikely (workstart != NULL)) - free (workstart); - workstart = NULL; - /* Write the following constant string. */ outstring (specs[nspecs_done].end_of_fmt, specs[nspecs_done].next_fmt - specs[nspecs_done].end_of_fmt); } all_done: - if (__glibc_unlikely (workstart != NULL)) - free (workstart); scratch_buffer_free (&argsbuf); scratch_buffer_free (&specsbuf); return done; |