Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Wed, 7 Nov 2018 14:54:02 -0600
From: CM Graff <cm0graff@...il.com>
To: musl@...ts.openwall.com
Subject: Re: printf family handling of INT_MAX +1 tested on aarch64

RIch,
It just produces a segfault on debian aarch64 in my test case. Whereas
INTMAX + 2 does not. So I thought it worth reporting.

graff@...b-debian-arm:~/hlibc-test/tests-emperical/musl$
./usr/bin/musl-gcc ../printf_overflow.c
graff@...b-debian-arm:~/hlibc-test/tests-emperical/musl$
./usr/bin/musl-gcc -static ../printf_overflow.c
graff@...b-debian-arm:~/hlibc-test/tests-emperical/musl$ ./a.out > logfile
Segmentation fault
graff@...b-debian-arm:~/hlibc-test/tests-emperical/musl$ uname -a
Linux hlib-debian-arm 4.9.0-8-arm64 #1 SMP Debian 4.9.110-3+deb9u6
(2018-10-08) aarch64 GNU/Linux
graff@...b-debian-arm:~/hlibc-test/tests-emperical/musl$

I can supply access to the 96 core 124 GB RAM aarch64 debian test box
if it would help reproduce the segfault. Just email me a public key if
you want access.

Graff

On 11/7/18, Rich Felker <dalias@...c.org> wrote:
> On Wed, Nov 07, 2018 at 01:33:13PM -0600, CM Graff wrote:
>> Hello everyone,
>>
>> The C standard states that:
>> "The number of characters or wide characters transmitted by a formatted
>> output
>> function (or written to an array, or that would have been written to an
>> array)
>> is greater
>> than INT_MAX" is undefined behavior.
>>
>> POSIX states that:
>>
>> "In addition, all forms of fprintf() shall fail if:
>>
>> [...]
>> [EOVERFLOW]
>>     [CX] [Option Start] The value to be returned is greater than
>> {INT_MAX}.
>> [Option End]
>> "
>>
>> Though arguments of over INT_MAX are undefined behavior it seems like
>> some
>> provisions have been made in musl to handle it, and the method for
>> handling
>> such appear similar in effect to that of glibc and freebsd's libc. INT_MAX
>> + 2
>> appears to represent this case, however INT_MAX + 1 produces a segfault on
>> my
>> aarch64 test box running debian version 9.5.
>                    ^^^^^^^^^^^^^^^^^^^^^^^^^^
>
> At first this sounded like you were using glibc, but based on the
> below test program it seems you're using the musl-gcc wrapper, then
> running musl binaries on the same box. Ok, this should work.
>
>> I do not have a suggested fix other than to either carefully inspect the
>> EOVERFLOW semantics or to mitigate the need for more complex mathematics
>> by
>> using a size_t as the primary counter for the stdio family instead of an
>> int.
>
> The counter is not incremented without seeing that the increment would
> not cause overflow. See vfprintf.c lines 447-450:
>
> 		/* This error is only specified for snprintf, but since it's
> 		 * unspecified for other forms, do the same. Stop immediately
> 		 * on overflow; otherwise %n could produce wrong results. */
> 		if (l > INT_MAX - cnt) goto overflow;
>
>> This segfault was discovered when testing my own small libc
>> (https://github.com/hlibc/hlibc) against the various robust production
>> grade
>> libc to understand more about how to properly handle EOVERFLOW and in
>> general
>> the cases of INT_MAX related undefined behavior for the formatted stdio
>> functions as per specified in the C standard and POSIX.
>>
>> I am not sure that handling this is an important case for musl, however I
>> thought it best to report the scenario as best I could describe it.
>
> I don't understand exactly what you're claiming is wrong. If it's a
> segfault, where does it occur?
>
>> Here is a script and a small C program to verify this segfault on
>> aarch64,
>> I apologize for not testing on other architectures but my time is limited
>> lately as I'm working toward my degree in mathematics.
>>
>> #!/bin/sh
>> git clone git://git.musl-libc.org/musl
>> cd musl
>> ../configure --prefix=$(pwd)/usr
>> make -j4 > log 2>&1
>> make install >> log 2>&1
>> ../usr/bin/musl-gcc -static ../printf_overflow.c
>> ../a.out > log2
>>
>>
>>
>> #include <stdio.h>
>> #include <limits.h>
>> #include <errno.h>
>> #include <string.h>
>> #include <stdlib.h>
>> int main(void)
>> {
>>         size_t i = INT_MAX;
>>         ++i;
>>         char *s = malloc(i);
>>         if (!(s))
>>         {
>>                 fprintf(stderr, "unable to allocate enough memory\n");
>>                 return 1;
>>         }
>>         memset(s, 'A', i - 1);
>>         s[i] = 0;
>>         /* make sure printf is not changed to puts() by the compiler */
>>         int len = printf("%s", s, 1);
>>
>>         if (errno == EOVERFLOW)
>>                 fprintf(stderr, "printf set EOVERFLOW\n");
>>         else
>>                 fprintf(stderr, "printf did not set EOVERFLOW\n");
>>
>>         fprintf(stderr, "printf returned %d\n", len);
>>         return 0;
>> }
>
> There is nothing in this test program that overflows. printf produces
> precisely INT_MAX bytes of output, which is representable, and
> therefore it succeeds and returns INT_MAX. I tested this on x86_64
> (it's not possible as written on 32-bit archs since INT_MAX+1 is not
> allocatable, although you could do similar tests like using %s%s to
> print the same INT_MAX/2-size string twice) and it worked as expected.
>
> Rich
>

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.