]> granicus.if.org Git - php/commitdiff
Fix for #73240 - Write out of bounds at number_format
authorStanislav Malyshev <stas@php.net>
Tue, 11 Oct 2016 06:42:50 +0000 (23:42 -0700)
committerStanislav Malyshev <stas@php.net>
Tue, 11 Oct 2016 06:42:50 +0000 (23:42 -0700)
ext/standard/math.c

index 5ffeff7a1815c8bbf5a73fb32884180e9e4253ea..8290b2044f8fc1266410ae72695e6a3f9424dd9c 100644 (file)
@@ -144,7 +144,7 @@ PHPAPI double _php_math_round(double value, int places, int mode) {
        if (!php_math_is_finite(value)) {
                return value;
        }
-       
+
        places = places < INT_MIN+1 ? INT_MIN+1 : places;
        precision_places = 14 - php_intlog10abs(value);
 
@@ -187,7 +187,7 @@ PHPAPI double _php_math_round(double value, int places, int mode) {
 
        /* round the temp value */
        tmp_value = php_round_helper(tmp_value, mode);
-       
+
        /* see if it makes sense to use simple division to round the value */
        if (abs(places) < 23) {
                if (places > 0) {
@@ -280,15 +280,15 @@ static double php_expm1(double x)
 
 /* {{{ proto int abs(int number)
    Return the absolute value of the number */
-PHP_FUNCTION(abs) 
+PHP_FUNCTION(abs)
 {
        zval **value;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
                return;
        }
        convert_scalar_to_number_ex(value);
-       
+
        if (Z_TYPE_PP(value) == IS_DOUBLE) {
                RETURN_DOUBLE(fabs(Z_DVAL_PP(value)));
        } else if (Z_TYPE_PP(value) == IS_LONG) {
@@ -300,14 +300,14 @@ PHP_FUNCTION(abs)
        }
        RETURN_FALSE;
 }
-/* }}} */ 
+/* }}} */
 
 /* {{{ proto float ceil(float number)
    Returns the next highest integer value of the number */
-PHP_FUNCTION(ceil) 
+PHP_FUNCTION(ceil)
 {
        zval **value;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
                return;
        }
@@ -328,7 +328,7 @@ PHP_FUNCTION(ceil)
 PHP_FUNCTION(floor)
 {
        zval **value;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &value) == FAILURE) {
                return;
        }
@@ -353,7 +353,7 @@ PHP_FUNCTION(round)
        long precision = 0;
        long mode = PHP_ROUND_HALF_UP;
        double return_val;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|ll", &value, &precision, &mode) == FAILURE) {
                return;
        }
@@ -410,7 +410,7 @@ PHP_FUNCTION(sin)
 PHP_FUNCTION(cos)
 {
        double num;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
                return;
        }
@@ -540,7 +540,7 @@ PHP_FUNCTION(asinh)
 PHP_FUNCTION(acosh)
 {
        double num;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &num) == FAILURE) {
                return;
        }
@@ -639,7 +639,7 @@ PHP_FUNCTION(exp)
 /* {{{ proto float expm1(float number)
    Returns exp(number) - 1, computed in a way that accurate even when the value of number is close to zero */
 /*
-   WARNING: this function is expermental: it could change its name or 
+   WARNING: this function is expermental: it could change its name or
    disappear in the next version of PHP!
 */
 PHP_FUNCTION(expm1)
@@ -654,9 +654,9 @@ PHP_FUNCTION(expm1)
 /* }}} */
 
 /* {{{ proto float log1p(float number)
-   Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */ 
+   Returns log(1 + number), computed in a way that accurate even when the value of number is close to zero */
 /*
-   WARNING: this function is expermental: it could change its name or 
+   WARNING: this function is expermental: it could change its name or
    disappear in the next version of PHP!
 */
 PHP_FUNCTION(log1p)
@@ -675,7 +675,7 @@ PHP_FUNCTION(log1p)
 PHP_FUNCTION(log)
 {
        double num, base = 0;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|d", &num, &base) == FAILURE) {
                return;
        }
@@ -683,7 +683,7 @@ PHP_FUNCTION(log)
                RETURN_DOUBLE(log(num));
        }
        if (base <= 0.0) {
-               php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");                             
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "base must be greater than 0");
                RETURN_FALSE;
        }
        if (base == 1) {
@@ -721,7 +721,7 @@ PHP_FUNCTION(sqrt)
 /* }}} */
 
 /* {{{ proto float hypot(float num1, float num2)
-   Returns sqrt(num1*num1 + num2*num2) */ 
+   Returns sqrt(num1*num1 + num2*num2) */
 PHP_FUNCTION(hypot)
 {
        double num1, num2;
@@ -783,12 +783,12 @@ PHPAPI long _php_math_basetolong(zval *arg, int base)
 
        for (i = Z_STRLEN_P(arg); i > 0; i--) {
                c = *s++;
-               
+
                digit = (c >= '0' && c <= '9') ? c - '0'
                        : (c >= 'A' && c <= 'Z') ? c - 'A' + 10
                        : (c >= 'a' && c <= 'z') ? c - 'a' + 10
                        : base;
-               
+
                if (digit >= base) {
                        continue;
                }
@@ -832,23 +832,23 @@ PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
 
        cutoff = LONG_MAX / base;
        cutlim = LONG_MAX % base;
-       
+
        for (i = Z_STRLEN_P(arg); i > 0; i--) {
                c = *s++;
 
                /* might not work for EBCDIC */
-               if (c >= '0' && c <= '9') 
+               if (c >= '0' && c <= '9')
                        c -= '0';
-               else if (c >= 'A' && c <= 'Z') 
+               else if (c >= 'A' && c <= 'Z')
                        c -= 'A' - 10;
-               else if (c >= 'a' && c <= 'z') 
+               else if (c >= 'a' && c <= 'z')
                        c -= 'a' - 10;
                else
                        continue;
 
                if (c >= base)
                        continue;
-               
+
                switch (mode) {
                case 0: /* Integer */
                        if (num < cutoff || (num == cutoff && c <= cutlim)) {
@@ -861,7 +861,7 @@ PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret)
                        /* fall-through */
                case 1: /* Float */
                        fnum = fnum * base + c;
-               }       
+               }
        }
 
        if (mode == 1) {
@@ -937,9 +937,9 @@ PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
 
                return estrndup(ptr, end - ptr);
        }
-       
+
        return _php_math_longtobase(arg, base);
-}      
+}
 /* }}} */
 
 /* {{{ proto int bindec(string binary_number)
@@ -947,7 +947,7 @@ PHPAPI char * _php_math_zvaltobase(zval *arg, int base TSRMLS_DC)
 PHP_FUNCTION(bindec)
 {
        zval **arg;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
                return;
        }
@@ -963,7 +963,7 @@ PHP_FUNCTION(bindec)
 PHP_FUNCTION(hexdec)
 {
        zval **arg;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
                return;
        }
@@ -979,7 +979,7 @@ PHP_FUNCTION(hexdec)
 PHP_FUNCTION(octdec)
 {
        zval **arg;
-       
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
                return;
        }
@@ -1050,7 +1050,7 @@ PHP_FUNCTION(base_convert)
                return;
        }
        convert_to_string_ex(number);
-       
+
        if (frombase < 2 || frombase > 36) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid `from base' (%ld)", frombase);
                RETURN_FALSE;
@@ -1065,10 +1065,10 @@ PHP_FUNCTION(base_convert)
        }
        result = _php_math_zvaltobase(&temp, tobase TSRMLS_CC);
        RETVAL_STRING(result, 0);
-} 
+}
 /* }}} */
 
-/* {{{ _php_math_number_format 
+/* {{{ _php_math_number_format
 */
 PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
 {
@@ -1077,13 +1077,13 @@ PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char tho
 
 static char *_php_math_number_format_ex_len(double d, int dec, char *dec_point,
                size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len,
-               int *result_len)
+               size_t *result_len)
 {
        char *tmpbuf = NULL, *resbuf;
        char *s, *t;  /* source, target */
        char *dp;
-       int integral;
-       int tmplen, reslen=0;
+       size_t integral;
+       size_t tmplen, reslen=0;
        int count=0;
        int is_negative=0;
 
@@ -1122,15 +1122,23 @@ static char *_php_math_number_format_ex_len(double d, int dec, char *dec_point,
 
        /* allow for thousand separators */
        if (thousand_sep) {
+               if (integral + thousand_sep_len * ((integral-1) / 3) < integral) {
+                       /* overflow */
+                       php_error_docref(NULL TSRMLS_CC, E_ERROR, "String overflow");
+               }
                integral += thousand_sep_len * ((integral-1) / 3);
        }
-       
+
        reslen = integral;
-       
+
        if (dec) {
                reslen += dec;
 
                if (dec_point) {
+                       if (reslen + dec_point < dec_point) {
+                               /* overflow */
+                               php_error_docref(NULL TSRMLS_CC, E_ERROR, "String overflow");
+                       }
                        reslen += dec_point_len;
                }
        }
@@ -1156,7 +1164,7 @@ static char *_php_math_number_format_ex_len(double d, int dec, char *dec_point,
                while (topad--) {
                        *t-- = '0';
                }
-               
+
                if (dp) {
                        s -= declen + 1; /* +1 to skip the point */
                        t -= declen;
@@ -1188,7 +1196,7 @@ static char *_php_math_number_format_ex_len(double d, int dec, char *dec_point,
        }
 
        efree(tmpbuf);
-       
+
        if (result_len) {
                *result_len = reslen;
        }
@@ -1213,17 +1221,21 @@ PHP_FUNCTION(number_format)
        char *thousand_sep = NULL, *dec_point = NULL;
        char thousand_sep_chr = ',', dec_point_chr = '.';
        int thousand_sep_len = 0, dec_point_len = 0;
-       
+       char *formatted;
+       size_t formatted_len;
+
        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d|ls!s!", &num, &dec, &dec_point, &dec_point_len, &thousand_sep, &thousand_sep_len) == FAILURE) {
                return;
        }
 
        switch(ZEND_NUM_ARGS()) {
        case 1:
-               RETURN_STRING(_php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr), 0);
+               formatted = _php_math_number_format(num, 0, dec_point_chr, thousand_sep_chr);
+               formatted_len = strlen(formatted);
                break;
        case 2:
-               RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
+               formatted = _php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr);
+               formatted_len = strlen(formatted);
                break;
        case 4:
                if (dec_point == NULL) {
@@ -1236,15 +1248,15 @@ PHP_FUNCTION(number_format)
                        thousand_sep_len = 1;
                }
 
-               Z_TYPE_P(return_value) = IS_STRING;
-               Z_STRVAL_P(return_value) = _php_math_number_format_ex_len(num, dec,
+               formatted = _php_math_number_format_ex_len(num, dec,
                                dec_point, dec_point_len, thousand_sep, thousand_sep_len,
-                               &Z_STRLEN_P(return_value));
+                               &formatted_len);
                break;
        default:
                WRONG_PARAM_COUNT;
-               break;
+               return;
        }
+       RETVAL_STRINGL_CHECK(formatted, formatted_len, 0);
 }
 /* }}} */