I believe the buffer overflow described below can only be triggered in 2.29 or before, because of the change in 2.30 to stop allowing memory allocations of over half the address space. However, the code in question in __printf_fp_l is unchanged and a buffer overflow might be appropriate to fix on older release branches.
The following test, on 32-bit systems with glibc 2.29 or earlier, has a buffer overrun if it is capable of setting the locale and allocating all the memory required by printf up to the point of the buffer overrun (which is about 5/6 of the address space; I only had the allocations succeed when using static linking).
if ((fp = fopen ("/dev/null", "w")) == NULL)
if (setlocale (LC_CTYPE, "en_US.UTF-8") == NULL)
fprintf (fp, "%I.*f", (int) (0xffffffff / 6), 1e100);
The relevant code in __printf_fp_l has been unchanged for some years. It computes the size of a buffer for converting to locale-specific outdigits ('I') flag.
size_t nbuffer = (2 + chars_needed * factor + decimal_len
+ ngroups * thousands_sep_len);
For a UTF-8 locale, "factor" is 6. The chars_needed * factor multiplication can overflow. chars_needed cannot be much more than INT_MAX (because precision for printf is limited to the size of int), so overflow is not possible on 64-bit systems. And it's limited to (size_t) -1 / sizeof (wchar_t) - 2, so overflow is not possible with "factor" less than 4. An allocation of size (2 + chars_needed) * sizeof (wchar_t) has to succeed, which effectively means the overflow is not possible with factor equal to 4, and means that with glibc 2.30 or later the overflow could not occur with factor less than 8, but the largest MB_CUR_MAX value for any locale in glibc is 6.
Ideally this would be addressed by changing memory management in __printf_fp (with smarter code there would never be any need to store more than 4933 = FLT128_MAX_10_EXP + 1 digits before the decimal point or 16494 = FLT128_MANT_DIG - FLT128_MIN_EXP digits after it, as all digits outside that range will always be 0), thereby helping with some of the other open bugs relating to memory usage with large precision (note that other printf code also allocates space equal to precision, however, though this code is allocating sizeof (wchar_t) * precision). But a smaller fix to check for overflow in this calculation might be more suitable for backporting to past release branches.
The master branch has been updated by Joseph Myers <email@example.com>:
Author: Joseph Myers <firstname.lastname@example.org>
Date: Tue Jul 7 14:54:12 2020 +0000
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.