This is the mail archive of the newlib@sources.redhat.com mailing list for the newlib project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RFA: fix vfscanf problem with long numbers


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.  */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]