Bug 30068 (CVE-2023-25139) - incorrect printf output for integers with thousands separator and width field (CVE-2023-25139)
Summary: incorrect printf output for integers with thousands separator and width field...
Status: RESOLVED FIXED
Alias: CVE-2023-25139
Product: glibc
Classification: Unclassified
Component: stdio (show other bugs)
Version: 2.37
: P2 critical
Target Milestone: 2.38
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-02-02 01:16 UTC by Vincent Lefèvre
Modified: 2023-02-08 01:32 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2023-02-02 00:00:00
carlos: security+


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Vincent Lefèvre 2023-02-02 01:16:18 UTC
Consider the following C program:

#include <stdio.h>
#include <locale.h>

int main (void)
{
  if (setlocale (LC_ALL, ""))
    {
      printf ("1234567890123:\n");
      printf ("%0+ -'13ld:\n", 1234567L);
    }
  return 0;
}

and try it with a locale that has a thousands separator, such as "LC_ALL=en_US.utf8".

With glibc up to 2.36, I get as expected:

1234567890123:
+1,234,567   :

But I got a report from Klaus Dittrich (following his bug reports against GNU MPFR at https://sympa.inria.fr/sympa/arc/mpfr/2023-01/msg00001.html and https://sympa.inria.fr/sympa/arc/mpfr/2023-01/msg00017.html) that with glibc git on 2023-01-17, one gets:

1234567890123:
+1,234,567     :

i.e. padding is done to width 15 instead of 13.

I've got another report that the MPFR test also fails with glibc 2.37, which is probably the above issue.
Comment 1 Vincent Lefèvre 2023-02-02 01:17:31 UTC
BTW, see also bug 23432 for other issues related to the thousands separator (but no regression like here).
Comment 2 Vincent Lefèvre 2023-02-02 02:25:34 UTC
I suppose that the same bug occurs with sprintf(), in which case it can yield a buffer overflow since additional characters are output. So I'm raising the importance.
Comment 3 Frederik Schwan 2023-02-02 07:51:30 UTC
I can confirm that mpfr (latest release + master) sprintf tests fail with glibc 2.37.
Comment 4 Vincent Lefèvre 2023-02-02 08:54:28 UTC
I suspect that the padding amount is computed ignoring the thousands separators, so that the increasing of the width would be equal to the total length of the thousands separator.
Comment 5 Xi Ruoyao 2023-02-02 12:40:28 UTC
The patch for #23432 (https://sourceware.org/pipermail/libc-alpha/2023-January/144847.html) fixes the testcase.
Comment 6 Carlos O'Donell 2023-02-02 14:27:15 UTC
Thanks for reporting this. Yes, this is a regression where after the refactor the implementation does not account for grouping characters during padding of the width.

We'll work to get this fixed on the release branch so the distributions can pickup the fix directly.

I'm marking this security plus since the width could be unexpected if you used a number of other APIs to pre-compute an expected with e.g. nl_langinfo, and digit counting.

I've asked Andreas if he's working on a v2, because the test case needs a dependency on the test locale generation:
https://sourceware.org/pipermail/libc-alpha/2023-February/145204.html
Comment 7 Siddhesh Poyarekar 2023-02-02 22:17:50 UTC
Confirmed that this could potentially cause a buffer overflow with sprintf, something like below.  This will occur in the corner case where an application computes the size of buffer to be exactly enough to fit the digits in question, but sprintf ends up writing a couple of extra bytes, hence going beyond bounds.

#include <stdio.h>
#include <locale.h>
#include <string.h>

int main (void)
{
  char buf[strlen ("1234567890123:") + 1];
  __builtin_memset (buf, 'x', sizeof (buf));
  if (setlocale (LC_ALL, ""))
    {
      printf ("1234567890123:\n");
      printf ("%0+ -'13ld:\n", 1234567L);
      sprintf (buf, "%0+ -'13ld:", 1234567L);
      for (size_t i = 0; i < strlen ("1234567890123:") + 1; i++)
	{
	  printf ("%c", buf[i]);
	}
      printf ("\n");
    }
  return 0;
}
Comment 8 Siddhesh Poyarekar 2023-02-02 22:19:15 UTC
Oops, hit send too soon.  To finish, building with _FORTIFY_SOURCE should catch this problem immediately:

e.g., build the above program with:

$ gcc -D_FORTIFY_SOURCE=1 -O -o sprintf-test sprintf-test.c

and run:

$ LOCPATH=$PWD/localedata LC_ALL=en_US.UTF-8 ./elf/ld-linux-x86-64.so.2 --library-path .:./math:./elf:./dlfcn:./nss:./nis:./rt:./resolv:./mathvec:./support:./crypt:./nptl ../sprintf-test
1234567890123:
+1,234,567     :
*** buffer overflow detected ***: terminated
Aborted (core dumped)
Comment 9 Carlos O'Donell 2023-02-06 15:24:34 UTC
This is now fixed in master for glibc 2.38.

I'm working to add a NEWS entry and then backport to release/2.37/master.
Comment 10 Carlos O'Donell 2023-02-06 18:03:45 UTC
Fixed by c980549cc6a1c03c23cc2fe3e7b0fe626a0364b0 for glibc 2.38.
Marking as required for 2.37.
Comment 11 Carlos O'Donell 2023-02-08 01:32:28 UTC
Fixed in release/2.37/master with:

commit 07b9521fc6369d000216b96562ff7c0ed32a16c4
Author: Carlos O'Donell <carlos@redhat.com>
Date:   Thu Jan 19 12:50:20 2023 +0100

    Account for grouping in printf width (bug 30068)
    
    This is a partial fix for mishandling of grouping when formatting
    integers.  It properly computes the width in the presence of grouping
    characters when the width is larger than the number of significant
    digits. The precision related issue is documented in bug 23432.
    
    Co-authored-by: Andreas Schwab <schwab@suse.de>
    (cherry picked from commit c980549cc6a1c03c23cc2fe3e7b0fe626a0364b0)