Skip to content

Commit 905cb37

Browse files
committed
Fix GH-20042: SEGV in array.c when error handler clobbers IAP object
get_ht_for_iap() emits a deprecation notice for object arguments, which can trigger a user error handler that modifies the by-reference variable. After the handler returns, the zval may no longer be an object, causing a segfault when accessing it as one. Re-check the zval type after emitting the deprecation and bail out if it was clobbered.
1 parent a8543df commit 905cb37

2 files changed

Lines changed: 34 additions & 4 deletions

File tree

ext/standard/array.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,10 @@ static inline HashTable *get_ht_for_iap(zval *zv, bool separate) {
922922
php_error_docref(NULL, E_DEPRECATED,
923923
"Calling %s() on an object is deprecated", get_active_function_name());
924924

925+
if (UNEXPECTED(Z_TYPE_P(zv) != IS_OBJECT)) {
926+
return NULL;
927+
}
928+
925929
zend_object *zobj = Z_OBJ_P(zv);
926930
if (separate && zobj->properties && UNEXPECTED(GC_REFCOUNT(zobj->properties) > 1)) {
927931
if (EXPECTED(!(GC_FLAGS(zobj->properties) & IS_ARRAY_IMMUTABLE))) {
@@ -982,7 +986,7 @@ PHP_FUNCTION(end)
982986
ZEND_PARSE_PARAMETERS_END();
983987

984988
HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
985-
if (zend_hash_num_elements(array) == 0) {
989+
if (!array || zend_hash_num_elements(array) == 0) {
986990
/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
987991
RETURN_FALSE;
988992
}
@@ -1004,7 +1008,7 @@ PHP_FUNCTION(prev)
10041008
ZEND_PARSE_PARAMETERS_END();
10051009

10061010
HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1007-
if (zend_hash_num_elements(array) == 0) {
1011+
if (!array || zend_hash_num_elements(array) == 0) {
10081012
/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
10091013
RETURN_FALSE;
10101014
}
@@ -1026,7 +1030,7 @@ PHP_FUNCTION(next)
10261030
ZEND_PARSE_PARAMETERS_END();
10271031

10281032
HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1029-
if (zend_hash_num_elements(array) == 0) {
1033+
if (!array || zend_hash_num_elements(array) == 0) {
10301034
/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
10311035
RETURN_FALSE;
10321036
}
@@ -1048,7 +1052,7 @@ PHP_FUNCTION(reset)
10481052
ZEND_PARSE_PARAMETERS_END();
10491053

10501054
HashTable *array = get_ht_for_iap(array_zv, /* separate */ true);
1051-
if (zend_hash_num_elements(array) == 0) {
1055+
if (!array || zend_hash_num_elements(array) == 0) {
10521056
/* array->nInternalPointer is already 0 if the array is empty, even after removing elements */
10531057
RETURN_FALSE;
10541058
}
@@ -1070,6 +1074,9 @@ PHP_FUNCTION(current)
10701074
ZEND_PARSE_PARAMETERS_END();
10711075

10721076
HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1077+
if (!array) {
1078+
RETURN_FALSE;
1079+
}
10731080
php_array_iter_return_current(return_value, array, true);
10741081
}
10751082
/* }}} */
@@ -1084,6 +1091,9 @@ PHP_FUNCTION(key)
10841091
ZEND_PARSE_PARAMETERS_END();
10851092

10861093
HashTable *array = get_ht_for_iap(array_zv, /* separate */ false);
1094+
if (!array) {
1095+
RETURN_NULL();
1096+
}
10871097
zval *entry = php_array_iter_seek_current(array, true);
10881098
if (EXPECTED(entry)) {
10891099
zend_hash_get_current_key_zval(array, return_value);
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
GH-20042 (SEGV in array.c when error handler clobbers IAP object argument)
3+
--FILE--
4+
<?php
5+
foreach (['prev', 'next', 'end', 'reset', 'current', 'key'] as $func) {
6+
$obj = new stdClass;
7+
set_error_handler(function () use (&$obj) {
8+
$obj = 0;
9+
});
10+
var_dump($func($obj));
11+
restore_error_handler();
12+
}
13+
?>
14+
--EXPECT--
15+
bool(false)
16+
bool(false)
17+
bool(false)
18+
bool(false)
19+
bool(false)
20+
NULL

0 commit comments

Comments
 (0)