]> granicus.if.org Git - php/commitdiff
re-apply patch for github PR #1695
authorThomas Punt <tpunt@hotmail.co.uk>
Tue, 12 Jan 2016 06:07:51 +0000 (07:07 +0100)
committerAnatol Belski <ab@php.net>
Tue, 12 Jan 2016 06:07:51 +0000 (07:07 +0100)
ext/standard/array.c
ext/standard/tests/array/range_bug71132.phpt [new file with mode: 0644]
ext/standard/tests/array/range_bug71197.phpt [new file with mode: 0644]

index 30ca0508a90f3b103af58b16eaecb10cd3e5e954..4152cc4b1f51239ce56dc52bd3851a9d2c48f258 100644 (file)
@@ -81,8 +81,6 @@
 #define INTERSECT_COMP_DATA_USER     1
 #define INTERSECT_COMP_KEY_INTERNAL  0
 #define INTERSECT_COMP_KEY_USER      1
-
-#define DOUBLE_DRIFT_FIX       0.000000000000001
 /* }}} */
 
 ZEND_DECLARE_MODULE_GLOBALS(array)
@@ -2061,13 +2059,25 @@ PHP_FUNCTION(array_fill_keys)
 }
 /* }}} */
 
-#define RANGE_CHECK_INIT_ARRAY(start, end) do { \
+#define RANGE_CHECK_DOUBLE_INIT_ARRAY(start, end) do { \
                double __calc_size = ((start - end) / step) + 1; \
-               if (fabs(__calc_size) >= (double)HT_MAX_SIZE) { \
-                       php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=%0.0f end=%0.0f", start > end ? end : start, start > end ? start : end); \
+               if (__calc_size >= (double)HT_MAX_SIZE) { \
+                       php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=%0.0f end=%0.0f", end, start); \
+                       RETURN_FALSE; \
+               } \
+               size = (uint32_t)__calc_size; \
+               array_init_size(return_value, size); \
+               zend_hash_real_init(Z_ARRVAL_P(return_value), 1); \
+       } while (0)
+
+#define RANGE_CHECK_LONG_INIT_ARRAY(start, end) do { \
+               zend_ulong __calc_size = (start - end) / lstep; \
+               if (__calc_size >= HT_MAX_SIZE - 1) { \
+                       php_error_docref(NULL, E_WARNING, "The supplied range exceeds the maximum array size: start=%pd end=%pd", end, start); \
                        RETURN_FALSE; \
                } \
-               array_init_size(return_value, (uint32_t)fabs(__calc_size)); \
+               size = (uint32_t)(__calc_size + 1); \
+               array_init_size(return_value, size); \
                zend_hash_real_init(Z_ARRVAL_P(return_value), 1); \
        } while (0)
 
@@ -2167,12 +2177,11 @@ PHP_FUNCTION(range)
                        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
                }
        } else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {
-               double low, high, value;
-               zend_long i;
+               double low, high;
+               uint32_t i, size;
 double_str:
                low = zval_get_double(zlow);
                high = zval_get_double(zhigh);
-               i = 0;
 
                if (zend_isinf(high) || zend_isinf(low)) {
                        php_error_docref(NULL, E_WARNING, "Invalid range supplied: start=%0.0f end=%0.0f", low, high);
@@ -2186,12 +2195,13 @@ double_str:
                                goto err;
                        }
 
-                       RANGE_CHECK_INIT_ARRAY(low, high);
+                       RANGE_CHECK_DOUBLE_INIT_ARRAY(low, high);
+
                        ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
-                       for (value = low; value >= (high - DOUBLE_DRIFT_FIX); value = low - (++i * step)) {
-                               Z_DVAL(tmp) = value;
-                               ZEND_HASH_FILL_ADD(&tmp);
-                       }
+                               for (i = 0; i < size; ++i) {
+                                       Z_DVAL(tmp) = low - (i * step);
+                                       ZEND_HASH_FILL_ADD(&tmp);
+                               }
                        } ZEND_HASH_FILL_END();
                } else if (high > low) {        /* Positive steps */
                        if (high - low < step || step <= 0) {
@@ -2199,10 +2209,11 @@ double_str:
                                goto err;
                        }
 
-                       RANGE_CHECK_INIT_ARRAY(high, low);
+                       RANGE_CHECK_DOUBLE_INIT_ARRAY(high, low);
+
                        ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
-                               for (value = low; value <= (high + DOUBLE_DRIFT_FIX); value = low + (++i * step)) {
-                                       Z_DVAL(tmp) = value;
+                               for (i = 0; i < size; ++i) {
+                                       Z_DVAL(tmp) = low + (i * step);
                                        ZEND_HASH_FILL_ADD(&tmp);
                                }
                        } ZEND_HASH_FILL_END();
@@ -2212,43 +2223,53 @@ double_str:
                        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
                }
        } else {
-               double low, high;
-               zend_long lstep;
+               zend_long low, high;
+               /* lstep is a ulong so that comparisons to it don't overflow, i.e. low - high < lstep */
+               zend_ulong lstep;
+               uint32_t i, size;
 long_str:
-               low = zval_get_double(zlow);
-               high = zval_get_double(zhigh);
-               lstep = (zend_long) step;
+               low = zval_get_long(zlow);
+               high = zval_get_long(zhigh);
+
+               if (step <= 0) {
+                       err = 1;
+                       goto err;
+               }
+
+               lstep = step;
 
                Z_TYPE_INFO(tmp) = IS_LONG;
                if (low > high) {               /* Negative steps */
-                       if (low - high < lstep || lstep <= 0) {
+                       if (low - high < lstep) {
                                err = 1;
                                goto err;
                        }
 
-                       RANGE_CHECK_INIT_ARRAY(low, high);
+                       RANGE_CHECK_LONG_INIT_ARRAY(low, high);
+
                        ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
-                               for (; low >= high; low -= lstep) {
-                                       Z_LVAL(tmp) = (zend_long)low;
+                               for (i = 0; i < size; ++i) {
+                                       Z_LVAL(tmp) = low - (i * lstep);
                                        ZEND_HASH_FILL_ADD(&tmp);
                                }
                        } ZEND_HASH_FILL_END();
                } else if (high > low) {        /* Positive steps */
-                       if (high - low < lstep || lstep <= 0) {
+                       if (high - low < lstep) {
                                err = 1;
                                goto err;
                        }
 
-                       RANGE_CHECK_INIT_ARRAY(high, low);
+                       RANGE_CHECK_LONG_INIT_ARRAY(high, low);
+
                        ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) {
-                               for (; low <= high; low += lstep) {
-                                       Z_LVAL(tmp) = (zend_long)low;
+                               for (i = 0; i < size; ++i) {
+                                       Z_LVAL(tmp) = low + (i * lstep);
                                        ZEND_HASH_FILL_ADD(&tmp);
                                }
                        } ZEND_HASH_FILL_END();
                } else {
                        array_init(return_value);
-                       Z_LVAL(tmp) = (zend_long)low;
+                       Z_LVAL(tmp) = low;
                        zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
                }
        }
@@ -2260,7 +2281,8 @@ err:
 }
 /* }}} */
 
-#undef RANGE_CHECK_INIT_ARRAY
+#undef RANGE_CHECK_DOUBLE_INIT_ARRAY
+#undef RANGE_CHECK_LONG_INIT_ARRAY
 
 static void php_array_data_shuffle(zval *array) /* {{{ */
 {
diff --git a/ext/standard/tests/array/range_bug71132.phpt b/ext/standard/tests/array/range_bug71132.phpt
new file mode 100644 (file)
index 0000000..5e3bcc8
--- /dev/null
@@ -0,0 +1,10 @@
+--TEST--
+Bug #71132 (range function produces 2 segfaults with long integers)
+--FILE--
+<?php
+var_dump(count(range(PHP_INT_MIN + 513, PHP_INT_MIN)));
+var_dump(count(range(PHP_INT_MIN, PHP_INT_MIN + 513)));
+?>
+--EXPECT--
+int(514)
+int(514)
diff --git a/ext/standard/tests/array/range_bug71197.phpt b/ext/standard/tests/array/range_bug71197.phpt
new file mode 100644 (file)
index 0000000..2031ec7
--- /dev/null
@@ -0,0 +1,8 @@
+--TEST--
+Bug #71197 (range function produces another 2 segfaults with long integers)
+--FILE--
+<?php
+range(PHP_INT_MIN, PHP_INT_MIN + 513, .01);
+range(PHP_INT_MIN + 513, PHP_INT_MIN, .01);
+?>
+--EXPECT--