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 15:31:21 -0500
From: Rich Felker <dalias@...c.org>
To: musl@...ts.openwall.com
Subject: Re: printf family handling of INT_MAX +1 tested on aarch64

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.