wez             Thu Jan  9 10:44:50 2003 EDT

  Added files:                 
    /php4/ext/standard/tests/math       bug21523.phpt 

  Modified files:              
    /php4/ext/standard  math.c 
  Log:
  Fix Bug #21523 - number_format could cause a memory allocation for a
  negative memory size in situations where the sprintf implementation of the
  host system generated less decimal places than were requested.
  
  Resolved this issue by making number_format examine the string returned
  by spprintf and have it pad to the correct number of decimal places.
  
  Added a test-case based on the bug report; the length of decimal places
  required to trigger this bug is sprintf implementation dependent; as the
  implementation is now using spprintf (provided by PHP), that number is
  78 digits (NDIG - 2).
  
  # I played with the idea of enhancing sprintf to do the equivalent, but
  # it was too much effort considering that the precision of floats/doubles
  # is not good enough to warrant it.
  
  # This fix could do with some QA from someone else to make sure there are
  # no memory bounds problems and then MFH it to PHP_4_3
  
  
  
Index: php4/ext/standard/math.c
diff -u php4/ext/standard/math.c:1.99 php4/ext/standard/math.c:1.100
--- php4/ext/standard/math.c:1.99       Tue Dec 31 11:07:46 2002
+++ php4/ext/standard/math.c    Thu Jan  9 10:44:49 2003
@@ -19,7 +19,7 @@
    +----------------------------------------------------------------------+
 */
 
-/* $Id: math.c,v 1.99 2002/12/31 16:07:46 sebastian Exp $ */
+/* $Id: math.c,v 1.100 2003/01/09 15:44:49 wez Exp $ */
 
 #include "php.h"
 #include "php_math.h"
@@ -980,57 +980,91 @@
 
 PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char 
thousand_sep)
 {
-       char *tmpbuf, *resbuf;
+       char *tmpbuf = NULL, *resbuf;
        char *s, *t;  /* source, target */
+       char *dp;
+       int integral;
        int tmplen, reslen=0;
        int count=0;
        int is_negative=0;
        
-       if (d<0) {
-               is_negative=1;
+       if (d < 0) {
+               is_negative = 1;
                d = -d;
        }
        dec = MAX(0, dec);
-       tmpbuf = (char *) emalloc(1+DBL_MAX_10_EXP+1+dec+1);
-       
-       tmplen=sprintf(tmpbuf, "%.*f", dec, d);
 
-       if (!isdigit((int)tmpbuf[0])) {
+       tmplen = spprintf(&tmpbuf, 0, "%.*f", dec, d);
+
+       if (tmpbuf == NULL || !isdigit((int)tmpbuf[0])) {
                return tmpbuf;
        }
 
-       if (dec) {
-               reslen = dec+1 + (tmplen-dec-1) + ((thousand_sep) ? (tmplen-1-dec-1)/3 
: 0);
+       /* calculate the length of the return buffer */
+       dp = strchr(tmpbuf, '.');
+       if (dp) {
+               integral = dp - tmpbuf;
        } else {
-               reslen = tmplen+((thousand_sep) ? (tmplen-1)/3 : 0);
+               /* no decimal point was found */
+               integral = tmplen;
+       }
+
+       /* allow for thousand separators */
+       if (thousand_sep) {
+               integral += integral / 3;
        }
+       
+       reslen = integral + 1 + dec;
+
+       /* add a byte for minus sign */
        if (is_negative) {
                reslen++;
        }
-       resbuf = (char *) emalloc(reslen+1);
-       
+       resbuf = (char *) emalloc(reslen+1); /* +1 for NUL terminator */
+
        s = tmpbuf+tmplen-1;
        t = resbuf+reslen;
-       *t-- = 0;
-       
+       *t-- = '\0';
+
+       /* copy the decimal places.
+        * Take care, as the sprintf implementation may return less places than
+        * we requested due to internal buffer limitations */
        if (dec) {
-               while (isdigit((int)*s)) {
-                       *t-- = *s--;
+               int declen = dp ? strlen(dp+1) : 0;
+               int topad = declen > 0 ? dec - declen : 0;
+
+               /* pad with '0's */
+               while (topad--) {
+                       *t-- = '0';
                }
-               *t-- = dec_point;  /* copy that dot */
+                       
+               /* now copy the chars after the point */
+               memcpy(t - declen + 1, dp + 1, declen);
+               
+               t -= declen;
+               s -= declen;
+
+               /* add decimal point */
+               *t-- = dec_point;
                s--;
        }
        
-       while(s>=tmpbuf) {
+       /* copy the numbers before the decimal place, adding thousand
+        * separator every three digits */
+       while(s >= tmpbuf) {
                *t-- = *s--;
                if (thousand_sep && (++count%3)==0 && s>=tmpbuf) {
                        *t-- = thousand_sep;
                }
        }
+
+       /* and a minus sign, if needed */
        if (is_negative) {
                *t-- = '-';
        }
+
        efree(tmpbuf);
+
        return resbuf;
 }
 

Index: php4/ext/standard/tests/math/bug21523.phpt
+++ php4/ext/standard/tests/math/bug21523.phpt
--TEST--
Bug #21523 number_format tries to allocate negative amount of memory
--FILE--
<?php // $Id: bug21523.phpt,v 1.1 2003/01/09 15:44:49 wez Exp $ vim600:syn=php

var_dump(number_format(-2000, 2768));
echo "OK";
?>
--EXPECT--
string(2775) 
"-2,000.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
OK



-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to