Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 19 additions & 6 deletions ext/spl/spl_iterators.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
}
Expand Down Expand Up @@ -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 */
Expand All @@ -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);
}
} /* }}} */
Expand Down
16 changes: 16 additions & 0 deletions ext/spl/tests/gh19577.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
--TEST--
GH-19577: Integer overflow in LimitIterator with small offset and PHP_INT_MAX count
--FILE--
<?php

$it = new ArrayIterator(array(0 => '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
Loading