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