Skip to content

Commit 4d888cf

Browse files
committed
Fix GH-12192: SimpleXML infinite loop when getName() is called within foreach
This happens because getName() resets the iterator to the start because it overwrites the iterator data. We add a version of get_first_node that does not overwrite the iterator data. Closes GH-12193.
1 parent 10f5a06 commit 4d888cf

File tree

3 files changed

+65
-8
lines changed

3 files changed

+65
-8
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ PHP NEWS
1111

1212
- SimpleXML:
1313
. Fixed bug GH-12170 (Can't use xpath with comments in SimpleXML). (nielsdos)
14+
. Fixed bug GH-12192 (SimpleXML infinite loop when getName() is called
15+
within foreach). (nielsdos)
1416

1517
28 Sep 2023, PHP 8.1.24
1618

ext/simplexml/simplexml.c

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ PHP_SXE_API zend_class_entry *sxe_get_element_class_entry(void) /* {{{ */
4545

4646
static php_sxe_object* php_sxe_object_new(zend_class_entry *ce, zend_function *fptr_count);
4747
static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data);
48+
static xmlNodePtr php_sxe_reset_iterator_no_clear_iter_data(php_sxe_object *sxe, int use_data);
4849
static xmlNodePtr php_sxe_iterator_fetch(php_sxe_object *sxe, xmlNodePtr node, int use_data);
4950
static void php_sxe_iterator_dtor(zend_object_iterator *iter);
5051
static int php_sxe_iterator_valid(zend_object_iterator *iter);
@@ -77,6 +78,7 @@ static void _node_as_zval(php_sxe_object *sxe, xmlNodePtr node, zval *value, SXE
7778
}
7879
/* }}} */
7980

81+
/* Important: this overwrites the iterator data, if you wish to keep it use php_sxe_get_first_node_non_destructive() instead! */
8082
static xmlNodePtr php_sxe_get_first_node(php_sxe_object *sxe, xmlNodePtr node) /* {{{ */
8183
{
8284
php_sxe_object *intern;
@@ -95,6 +97,15 @@ static xmlNodePtr php_sxe_get_first_node(php_sxe_object *sxe, xmlNodePtr node) /
9597
}
9698
/* }}} */
9799

100+
static xmlNodePtr php_sxe_get_first_node_non_destructive(php_sxe_object *sxe, xmlNodePtr node)
101+
{
102+
if (sxe && sxe->iter.type != SXE_ITER_NONE) {
103+
return php_sxe_reset_iterator_no_clear_iter_data(sxe, false);
104+
} else {
105+
return node;
106+
}
107+
}
108+
98109
static inline int match_ns(php_sxe_object *sxe, xmlNodePtr node, xmlChar *name, int prefix) /* {{{ */
99110
{
100111
if (name == NULL && (node->ns == NULL || node->ns->prefix == NULL)) {
@@ -1625,7 +1636,7 @@ PHP_METHOD(SimpleXMLElement, getName)
16251636
sxe = Z_SXEOBJ_P(ZEND_THIS);
16261637

16271638
GET_NODE(sxe, node);
1628-
node = php_sxe_get_first_node(sxe, node);
1639+
node = php_sxe_get_first_node_non_destructive(sxe, node);
16291640
if (node) {
16301641
namelen = xmlStrlen(node->name);
16311642
RETURN_STRINGL((char*)node->name, namelen);
@@ -2450,15 +2461,9 @@ static xmlNodePtr php_sxe_iterator_fetch(php_sxe_object *sxe, xmlNodePtr node, i
24502461
}
24512462
/* }}} */
24522463

2453-
static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data) /* {{{ */
2464+
static xmlNodePtr php_sxe_reset_iterator_no_clear_iter_data(php_sxe_object *sxe, int use_data)
24542465
{
24552466
xmlNodePtr node;
2456-
2457-
if (!Z_ISUNDEF(sxe->iter.data)) {
2458-
zval_ptr_dtor(&sxe->iter.data);
2459-
ZVAL_UNDEF(&sxe->iter.data);
2460-
}
2461-
24622467
GET_NODE(sxe, node)
24632468

24642469
if (node) {
@@ -2471,10 +2476,23 @@ static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data) /* {
24712476
case SXE_ITER_ATTRLIST:
24722477
node = (xmlNodePtr) node->properties;
24732478
}
2479+
if (use_data) {
2480+
ZEND_ASSERT(Z_ISUNDEF(sxe->iter.data));
2481+
}
24742482
return php_sxe_iterator_fetch(sxe, node, use_data);
24752483
}
24762484
return NULL;
24772485
}
2486+
2487+
static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data) /* {{{ */
2488+
{
2489+
if (!Z_ISUNDEF(sxe->iter.data)) {
2490+
zval_ptr_dtor(&sxe->iter.data);
2491+
ZVAL_UNDEF(&sxe->iter.data);
2492+
}
2493+
2494+
return php_sxe_reset_iterator_no_clear_iter_data(sxe, use_data);
2495+
}
24782496
/* }}} */
24792497

24802498
zend_object_iterator *php_sxe_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */

ext/simplexml/tests/gh12192.phpt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
GH-12192 (SimpleXML infinite loop when getName() is called within foreach)
3+
--EXTENSIONS--
4+
simplexml
5+
--FILE--
6+
<?php
7+
8+
$xml = "<root><a>1</a><a>2</a></root>";
9+
$xml = simplexml_load_string($xml);
10+
11+
$a = $xml->a;
12+
13+
foreach ($a as $test) {
14+
echo "Iteration\n";
15+
var_dump($a->key());
16+
var_dump($a->getName());
17+
var_dump((string) $test);
18+
}
19+
20+
var_dump($a);
21+
22+
?>
23+
--EXPECT--
24+
Iteration
25+
string(1) "a"
26+
string(1) "a"
27+
string(1) "1"
28+
Iteration
29+
string(1) "a"
30+
string(1) "a"
31+
string(1) "2"
32+
object(SimpleXMLElement)#2 (2) {
33+
[0]=>
34+
string(1) "1"
35+
[1]=>
36+
string(1) "2"
37+
}

0 commit comments

Comments
 (0)