diff --git a/NEWS b/NEWS index ccd67d5a27efd..db04f22ca66dc 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,8 @@ PHP NEWS - Standard: . Fixed bug GH-16649 (UAF during array_splice). (alexandre-daubois) + . Fixed bug GH-19577 (Avoid integer overflow when using a small offset + and PHP_INT_MAX with LimitIterator). (alexandre-daubois) - Tidy: . Fixed GH-19021 build issue with libtidy in regard of tidyOptIsReadonly diff --git a/ext/spl/spl_iterators.c b/ext/spl/spl_iterators.c index d41dbee81c381..9621a7ef747e5 100644 --- a/ext/spl/spl_iterators.c +++ b/ext/spl/spl_iterators.c @@ -2209,14 +2209,27 @@ static zend_object *spl_dual_it_new(zend_class_entry *class_type) } /* }}} */ +/* Returns the relative position for the current iterator position. */ +static zend_long spl_limit_it_relative_pos(spl_dual_it_object *intern) +{ + return intern->current.pos - intern->u.limit.offset; +} + +/* Returns the relative position for an arbitrary position. */ +static zend_long spl_limit_it_relative_pos_for(spl_dual_it_object *intern, zend_long pos) +{ + return pos - intern->u.limit.offset; +} + static inline int spl_limit_it_valid(spl_dual_it_object *intern) { /* FAILURE / SUCCESS */ - if (intern->u.limit.count != -1 && intern->current.pos >= intern->u.limit.offset + intern->u.limit.count) { + if (intern->u.limit.count != -1 && + spl_limit_it_relative_pos(intern) >= intern->u.limit.count) { return FAILURE; - } else { - return spl_dual_it_valid(intern); } + + return spl_dual_it_valid(intern); } static inline void spl_limit_it_seek(spl_dual_it_object *intern, zend_long pos) @@ -2228,7 +2241,7 @@ static inline void spl_limit_it_seek(spl_dual_it_object *intern, zend_long pos) zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is below the offset " ZEND_LONG_FMT, pos, intern->u.limit.offset); return; } - if (pos - intern->u.limit.offset >= intern->u.limit.count && intern->u.limit.count != -1) { + if (spl_limit_it_relative_pos_for(intern, pos) >= intern->u.limit.count && intern->u.limit.count != -1) { zend_throw_exception_ex(spl_ce_OutOfBoundsException, 0, "Cannot seek to " ZEND_LONG_FMT " which is behind offset " ZEND_LONG_FMT " plus count " ZEND_LONG_FMT, pos, intern->u.limit.offset, intern->u.limit.count); return; } @@ -2289,7 +2302,7 @@ PHP_METHOD(LimitIterator, valid) SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS); /* RETURN_BOOL(spl_limit_it_valid(intern) == SUCCESS);*/ - RETURN_BOOL((intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) && Z_TYPE(intern->current.data) != IS_UNDEF); + RETURN_BOOL((intern->u.limit.count == -1 || spl_limit_it_relative_pos(intern) < intern->u.limit.count) && Z_TYPE(intern->current.data) != IS_UNDEF); } /* }}} */ /* {{{ Move the iterator forward */ @@ -2304,7 +2317,7 @@ PHP_METHOD(LimitIterator, next) SPL_FETCH_AND_CHECK_DUAL_IT(intern, ZEND_THIS); spl_dual_it_next(intern, 1); - if (intern->u.limit.count == -1 || intern->current.pos < intern->u.limit.offset + intern->u.limit.count) { + if (intern->u.limit.count == -1 || spl_limit_it_relative_pos(intern) < intern->u.limit.count) { spl_dual_it_fetch(intern, 1); } } /* }}} */ diff --git a/ext/spl/tests/gh19577.phpt b/ext/spl/tests/gh19577.phpt new file mode 100644 index 0000000000000..641cfa57e0651 --- /dev/null +++ b/ext/spl/tests/gh19577.phpt @@ -0,0 +1,16 @@ +--TEST-- +GH-19577: Integer overflow in LimitIterator with small offset and PHP_INT_MAX count +--FILE-- + 'A', 1 => 'B', 2 => 'C', 3 => 'D')); +$it = new LimitIterator($it, 2, PHP_INT_MAX); + +foreach($it as $val => $key) { + echo "Key: $val, Value: $key\n"; +} + +?> +--EXPECT-- +Key: 2, Value: C +Key: 3, Value: D