glibc/stdio-common/bug22.c

79 lines
1.7 KiB
C
Raw Normal View History

/* BZ #5424 */
#include <stdio.h>
#include <errno.h>
#include <libc-diag.h>
/* INT_MAX + 1 */
#define N 2147483648
/* (INT_MAX / 2) + 2 */
#define N2 1073741825
/* INT_MAX - 3 */
#define N3 2147483644
#define STRINGIFY(S) #S
#define MAKE_STR(S) STRINGIFY(S)
#define SN MAKE_STR(N)
#define SN2 MAKE_STR(N2)
#define SN3 MAKE_STR(N3)
static int
do_test (void)
{
int ret;
FILE *fp = fopen ("/dev/null", "w");
if (fp == NULL)
{
puts ("cannot open /dev/null");
return 1;
}
/* GCC 9 warns about output of more than INT_MAX characters; this is
deliberately tested here. */
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (7, 0)
DIAG_IGNORE_NEEDS_COMMENT (9, "-Wformat-overflow=");
#endif
ret = fprintf (fp, "%" SN "d", 1);
DIAG_POP_NEEDS_COMMENT;
printf ("ret = %d\n", ret);
if (ret != -1 || errno != EOVERFLOW)
return 1;
/* GCC 9 warns about output of more than INT_MAX characters; this is
deliberately tested here. */
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (7, 0)
DIAG_IGNORE_NEEDS_COMMENT (9, "-Wformat-overflow=");
#endif
ret = fprintf (fp, "%." SN "d", 1);
DIAG_POP_NEEDS_COMMENT;
printf ("ret = %d\n", ret);
if (ret != -1 || errno != EOVERFLOW)
return 1;
ret = fprintf (fp, "%." SN3 "d", 1);
printf ("ret = %d\n", ret);
Remove most vfprintf width/precision-dependent allocations (bug 14231, bug 26211). The vfprintf implementation (used for all printf-family functions) contains complicated logic to allocate internal buffers of a size depending on the width and precision used for a format, using either malloc or alloca depending on that size, and with consequent checks for size overflow and allocation failure. As noted in bug 26211, the version of that logic used when '$' plus argument number formats are in use is missing the overflow checks, which can result in segfaults (quite possibly exploitable, I didn't try to work that out) when the width or precision is in the range 0x7fffffe0 through 0x7fffffff (maybe smaller values as well in the wprintf case on 32-bit systems, when the multiplication by sizeof (CHAR_T) can overflow). All that complicated logic in fact appears to be useless. As far as I can tell, there has been no need (outside the floating-point printf code, which does its own allocations) for allocations depending on width or precision since commit 3e95f6602b226e0de06aaff686dc47b282d7cc16 ("Remove limitation on size of precision for integers", Sun Sep 12 21:23:32 1999 +0000). Thus, this patch removes that logic completely, thereby fixing both problems with excessive allocations for large width and precision for non-floating-point formats, and the problem with missing overflow checks with such allocations. Note that this does have the consequence that width and precision up to INT_MAX are now allowed where previously INT_MAX / sizeof (CHAR_T) - EXTSIZ or more would have been rejected, so could potentially expose any other overflows where the value would previously have been rejected by those removed checks. I believe this completely fixes bugs 14231 and 26211. Excessive allocations are still possible in the floating-point case (bug 21127), as are other integer or buffer overflows (see bug 26201). This does not address the cases where a precision larger than INT_MAX (embedded in the format string) would be meaningful without printf's return value overflowing (when it's used with a string format, or %g without the '#' flag, so the actual output will be much smaller), as mentioned in bug 17829 comment 8; using size_t internally for precision to handle that case would be complicated by struct printf_info being a public ABI. Nor does it address the matter of an INT_MIN width being negated (bug 17829 comment 7; the same logic appears a second time in the file as well, in the form of multiplying by -1). There may be other sources of memory allocations with malloc in printf functions as well (bug 24988, bug 16060). From inspection, I think there are also integer overflows in two copies of "if ((width -= len) < 0)" logic (where width is int, len is size_t and a very long string could result in spurious padding being output on a 32-bit system before printf overflows the count of output characters). Tested for x86-64 and x86.
2020-07-07 14:54:12 +00:00
if (ret != N3)
return 1;
/* GCC 9 warns about output of more than INT_MAX characters; this is
deliberately tested here. */
DIAG_PUSH_NEEDS_COMMENT;
#if __GNUC_PREREQ (7, 0)
DIAG_IGNORE_NEEDS_COMMENT (9, "-Wformat-overflow=");
#endif
ret = fprintf (fp, "%" SN2 "d%" SN2 "d", 1, 1);
DIAG_POP_NEEDS_COMMENT;
printf ("ret = %d\n", ret);
return ret != -1 || errno != EOVERFLOW;
}
#define TIMEOUT 60
#define TEST_FUNCTION do_test ()
#include "../test-skeleton.c"