RFA: fix vfscanf problem with long numbers
Jeff Johnston
jjohnstn@redhat.com
Thu Apr 22 22:00:00 GMT 2004
Thanks Joern. I will massage the fix to apply to the powerpc version and then
check it in.
-- Jeff J.
Joern Rennecke wrote:
> The following testcase fails with newlib on configurations without
> long doubles > 64 bit (e.g. sh-elf) because the buffer used by vsscanf
> overflows:
>
> #include <string.h>
> #include <stdlib.h>
> #include <stdio.h>
>
> int
> main ()
> {
> char s[1100];
> int i;
> double d;
>
> memset (s, '0', 1024);
> strcpy (s+1024, "42");
> sscanf(s, "%d", &i);
> if (i != 42)
> abort ();
> s[1] = '.';
> strcpy (s+1024, "1e1023");
> sscanf(s, "%lf", &d);
> if (d != 1.0)
> abort ();
> exit (0);
> }
>
> The floating point failure is actually a regression intruduced by this
> patch:
>
> 2003-03-20 Jeff Johnston <jjohnstn@redhat.com>
>
> * libc/stdio/vfscanf.c (__svfscanf_r): For floating point conversion,
> count all characters used to create number against maximum width.
> * libc/machine/powerpc/vfscanf.c (__svfscanf_r): Ditto.
>
> The following patch both this regression and the long-standing failure
> for integers:
>
> 2004-04-21 J"orn Rennecke <joern.rennecke@superh.com>
>
> * libc/stdio/vfscanf.c (NNZDIGITS): New define.
> (__svfscanf_r): In integer conversions, leave out leading zeroes
> which are not part of a base prefix.
> Keep track of width truncation to fit into buf, not counting left-out
> zeroes against width till the truncation has been compensated for.
>
> Index: vfscanf.c
> ===================================================================
> RCS file: /cvs/src/src/newlib/libc/stdio/vfscanf.c,v
> retrieving revision 1.18
> diff -p -r1.18 vfscanf.c
> *** vfscanf.c 2 Apr 2004 00:59:17 -0000 1.18
> --- vfscanf.c 21 Apr 2004 16:08:01 -0000
> *************** extern _LONG_DOUBLE _strtold _PARAMS((ch
> *** 179,184 ****
> --- 179,185 ----
>
> #define PFXOK 0x200 /* 0x prefix is (still) legal */
> #define NZDIGITS 0x400 /* no zero digits detected */
> + #define NNZDIGITS 0x800 /* no non-zero digits detected */
>
> /*
> * Conversion types.
> *************** __svfscanf_r (rptr, fp, fmt0, ap)
> *** 759,775 ****
> continue;
>
> case CT_INT:
> /* scan an integer as if by strtol/strtoul */
> #ifdef hardway
> if (width == 0 || width > sizeof (buf) - 1)
> - width = sizeof (buf) - 1;
> #else
> /* size_t is unsigned, hence this optimisation */
> ! if (--width > sizeof (buf) - 2)
> ! width = sizeof (buf) - 2;
> ! width++;
> #endif
> ! flags |= SIGNOK | NDIGITS | NZDIGITS;
> for (p = buf; width; width--)
> {
> c = *fp->_p;
> --- 760,779 ----
> continue;
>
> case CT_INT:
> + {
> /* scan an integer as if by strtol/strtoul */
> + unsigned width_left = 0;
> #ifdef hardway
> if (width == 0 || width > sizeof (buf) - 1)
> #else
> /* size_t is unsigned, hence this optimisation */
> ! if (width - 1 > sizeof (buf) - 2)
> #endif
> ! {
> ! width_left = width - (sizeof (buf) - 1);
> ! width = sizeof (buf) - 1;
> ! }
> ! flags |= SIGNOK | NDIGITS | NZDIGITS | NNZDIGITS;
> for (p = buf; width; width--)
> {
> c = *fp->_p;
> *************** __svfscanf_r (rptr, fp, fmt0, ap)
> *** 789,804 ****
> * will turn it off if we have scanned any nonzero digits).
> */
> case '0':
> if (base == 0)
> {
> base = 8;
> flags |= PFXOK;
> }
> if (flags & NZDIGITS)
> ! flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
> ! else
> ! flags &= ~(SIGNOK | PFXOK | NDIGITS);
> ! goto ok;
>
> /* 1 through 7 always legal */
> case '1':
> --- 793,817 ----
> * will turn it off if we have scanned any nonzero digits).
> */
> case '0':
> + if (! (flags & NNZDIGITS))
> + goto ok;
> if (base == 0)
> {
> base = 8;
> flags |= PFXOK;
> }
> if (flags & NZDIGITS)
> ! {
> ! flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
> ! goto ok;
> ! }
> ! flags &= ~(SIGNOK | PFXOK | NDIGITS);
> ! if (width_left)
> ! {
> ! width_left--;
> ! width++;
> ! }
> ! goto skip;
>
> /* 1 through 7 always legal */
> case '1':
> *************** __svfscanf_r (rptr, fp, fmt0, ap)
> *** 809,815 ****
> case '6':
> case '7':
> base = basefix[base];
> ! flags &= ~(SIGNOK | PFXOK | NDIGITS);
> goto ok;
>
> /* digits 8 and 9 ok iff decimal or hex */
> --- 822,828 ----
> case '6':
> case '7':
> base = basefix[base];
> ! flags &= ~(SIGNOK | PFXOK | NDIGITS | NNZDIGITS);
> goto ok;
>
> /* digits 8 and 9 ok iff decimal or hex */
> *************** __svfscanf_r (rptr, fp, fmt0, ap)
> *** 818,824 ****
> base = basefix[base];
> if (base <= 8)
> break; /* not legal here */
> ! flags &= ~(SIGNOK | PFXOK | NDIGITS);
> goto ok;
>
> /* letters ok iff hex */
> --- 831,837 ----
> base = basefix[base];
> if (base <= 8)
> break; /* not legal here */
> ! flags &= ~(SIGNOK | PFXOK | NDIGITS | NNZDIGITS);
> goto ok;
>
> /* letters ok iff hex */
> *************** __svfscanf_r (rptr, fp, fmt0, ap)
> *** 837,843 ****
> /* no need to fix base here */
> if (base <= 10)
> break; /* not legal here */
> ! flags &= ~(SIGNOK | PFXOK | NDIGITS);
> goto ok;
>
> /* sign ok only as first character */
> --- 850,856 ----
> /* no need to fix base here */
> if (base <= 10)
> break; /* not legal here */
> ! flags &= ~(SIGNOK | PFXOK | NDIGITS | NNZDIGITS);
> goto ok;
>
> /* sign ok only as first character */
> *************** __svfscanf_r (rptr, fp, fmt0, ap)
> *** 872,877 ****
> --- 885,891 ----
> * c is legal: store it and look at the next.
> */
> *p++ = c;
> + skip:
> if (--fp->_r > 0)
> fp->_p++;
> else
> *************** __svfscanf_r (rptr, fp, fmt0, ap)
> *** 939,945 ****
> }
> nread += p - buf;
> break;
> !
> #ifdef FLOATING_POINT
> case CT_FLOAT:
> {
> --- 953,959 ----
> }
> nread += p - buf;
> break;
> ! }
> #ifdef FLOATING_POINT
> case CT_FLOAT:
> {
> *************** __svfscanf_r (rptr, fp, fmt0, ap)
> *** 951,965 ****
> long leading_zeroes = 0;
> long zeroes, exp_adjust;
> char *exp_start = NULL;
> #ifdef hardway
> if (width == 0 || width > sizeof (buf) - 1)
> - width = sizeof (buf) - 1;
> #else
> /* size_t is unsigned, hence this optimisation */
> ! if (--width > sizeof (buf) - 2)
> ! width = sizeof (buf) - 2;
> ! width++;
> #endif
> flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
> zeroes = 0;
> exp_adjust = 0;
> --- 965,981 ----
> long leading_zeroes = 0;
> long zeroes, exp_adjust;
> char *exp_start = NULL;
> + unsigned width_left = 0;
> #ifdef hardway
> if (width == 0 || width > sizeof (buf) - 1)
> #else
> /* size_t is unsigned, hence this optimisation */
> ! if (width - 1 > sizeof (buf) - 2)
> #endif
> + {
> + width_left = width - (sizeof (buf) - 1);
> + width = sizeof (buf) - 1;
> + }
> flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
> zeroes = 0;
> exp_adjust = 0;
> *************** __svfscanf_r (rptr, fp, fmt0, ap)
> *** 978,983 ****
> --- 994,1004 ----
> {
> flags &= ~SIGNOK;
> zeroes++;
> + if (width_left)
> + {
> + width_left--;
> + width++;
> + }
> goto fskip;
> }
> /* Fall through. */
More information about the Newlib
mailing list