Bug 4438 - vfprintf() segfault with multibyte string and long precision
Summary: vfprintf() segfault with multibyte string and long precision
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Ulrich Drepper
Depends on:
Reported: 2007-04-30 13:46 UTC by Victor Stinner
Modified: 2007-05-01 13:09 UTC (History)
2 users (show)

See Also:
Last reconfirmed:

fix for that bug (360 bytes, patch)
2007-04-30 13:56 UTC, Pierre Habouzit
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Victor Stinner 2007-04-30 13:46:18 UTC
(Since the bug has been reported on Debian and Fedora Core, I create a bug 
report on libc bugtracker)


I use your personnal emails because the bug might be a security vulnerability 
(I don't know Linux kernel enough to guess). If it is not, I can open a bug 
report on Bugzilla if you would like to.

I found a bug in dpkg program (from apt-get of Debian project):
   COLUMNS=10000000 dpkg -l
=> Crash with segfault (SIGSEGV)

After long investigation (around one week :-)), I'm certain that the bug comes
from GNU libc. The crash is not specific to this program, any program allowing
to change format string of printf() may crash. Smallest C testcase:
#include <stdlib.h>
#include <stdio.h>
#include <locale.h>

int main()
    setlocale (LC_CTYPE, "");
    printf("%-1.30500200s\n", "Hello");
    return 0;

If your locale is not UTF-8, specify another multibyte locale to setlocale(). 
The value "30500200" just have to be bigger than current stack size limit.

You can also try with bash/core-utils printf:
printf '%-1.25000000s' 'Hello'

The bug is located in stdio-common/vfprintf.c, macro "process_string_arg", in 
this block:
   if (prec != -1)
       /* Search for the end of the string, but don't search past
          the length (in bytes) specified by the precision.  Also
          don't use incomplete characters.  */
         len = __strnlen (string, prec);
           /* In case we have a multibyte character set the
              situation is more compilcated.  We must not copy
              bytes at the end which form an incomplete character. */
           wchar_t ignore[prec];
           const char *str2 = string;
           mbstate_t ps;

           memset (&ps, '\0', sizeof (ps));
           if (__mbsnrtowcs (ignore, &str2, prec, prec, &ps)
        == (size_t) -1)
        done = -1;
        goto all_done;
           if (str2 == NULL)
             len = strlen (string);
             len = str2 - string - (ps.__count & 7);
     len = strlen (string);

If 1 < prec and 1 < LC_CTYPE[_NL_CTYPE_MB_CUR_MAX], we go in "complicated" 
block :-)

Now imagine that prec is equal to 30500200: 30 MB will be "allocated" on the 
stack (by "wchar_t ignore[prec]") whereas Linux use 8 MB (in default config) 
for stack limit. Stack *should* grow up/down, but on my compute (i386) gcc 
just use "sub $eax, $esp" instruction to allocated memory and Linux just 
raises the signal SIGSEGV.

I don't know enough locale API (mbsnrtowcs() function) to fix the bug.

Victor Stinner


Other bug report of the same bug:
Comment 1 Pierre Habouzit 2007-04-30 13:56:32 UTC
Created attachment 1742 [details]
fix for that bug

FWIW here is a patch that should fix it. As you can guess, 'ignore' is an
ignored variable, hence __mbsnrtowcs is only used for mbs validation. So
passing NULL will work.

(yes I know that if passing NULL __mbsnrtowcs does not respect 'len' but it's
not relevant here, as we want to check 'spec' bytes from the mb sequence, and
the wchar_t buffer is made on purpose of 'spec' wchar_t's, so the 'len'
stopping condition will never be triggered, hence passing NULL as a dst will
Comment 2 Pierre Habouzit 2007-04-30 18:40:01 UTC
(In reply to comment #1)
> Created an attachment (id=1742)
> fix for that bug

except that it does not work, presumably because when passing NULL as dst, the 
mbstate is not updated.
The current code forcing the conversion into a wchar string seems quite sloppy, 
but I see no obvious way to deal with it. So a better patch will need to be 
worked out :|
Comment 3 Ulrich Drepper 2007-05-01 05:12:42 UTC
I've checked in a patch.

And next time, don't mess with priorities and severity.  This is not something
to be guessed by the submitter.
Comment 4 Victor Stinner 2007-05-01 14:09:16 UTC
Thank you for your quick reaction ;-) Sorry for severity/priority, I will 
leave them with default value next time.
Comment 5 cvs-commit@gcc.gnu.org 2007-07-12 15:15:15 UTC
Subject: Bug 4438

CVSROOT:	/cvs/glibc
Module name:	libc
Branch: 	glibc-2_5-branch
Changes by:	jakub@sourceware.org	2007-07-12 15:15:03

Modified files:
	.              : ChangeLog 
	stdio-common   : test-vfprintf.c vfprintf.c 

Log message:
	2007-05-04  Ulrich Drepper  <drepper@redhat.com>
	* stdio-common/vfprintf.c (process_string_arg): Adjust call to
	__mbsnrtowcs after last change.
	2007-05-02  Jakub Jelinek  <jakub@redhat.com>
	* stdio-common/vfprintf.c (process_string_arg): Use a VLA rather than
	fixed length array for ignore.
	2007-04-30  Ulrich Drepper  <drepper@redhat.com>
	[BZ #4438]
	* stdio-common/vfprintf.c (process_string_arg): Don't overflow the
	stack for large precisions.
	* stdio-common/test-vfprintf.c (main): Add test for large