Skip to content

Commit 1a4e401

Browse files
committedSep 20, 2023
Fix bug #55098: SimpleXML iteration produces infinite loop
Closes GH-12247.
1 parent d61efdf commit 1a4e401

File tree

4 files changed

+121
-47
lines changed

4 files changed

+121
-47
lines changed
 

‎NEWS

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ PHP NEWS
2525
var_dump/print_r). (nielsdos)
2626
. Fixed bug GH-12208 (SimpleXML infinite loop when a cast is used inside a
2727
foreach). (nielsdos)
28+
. Fixed bug #55098 (SimpleXML iteration produces infinite loop). (nielsdos)
2829

2930
28 Sep 2023, PHP 8.1.24
3031

‎ext/simplexml/simplexml.c

+26-45
Original file line numberDiff line numberDiff line change
@@ -78,25 +78,6 @@ static void _node_as_zval(php_sxe_object *sxe, xmlNodePtr node, zval *value, SXE
7878
}
7979
/* }}} */
8080

81-
/* Important: this overwrites the iterator data, if you wish to keep it use php_sxe_get_first_node_non_destructive() instead! */
82-
static xmlNodePtr php_sxe_get_first_node(php_sxe_object *sxe, xmlNodePtr node) /* {{{ */
83-
{
84-
php_sxe_object *intern;
85-
xmlNodePtr retnode = NULL;
86-
87-
if (sxe && sxe->iter.type != SXE_ITER_NONE) {
88-
php_sxe_reset_iterator(sxe, 1);
89-
if (!Z_ISUNDEF(sxe->iter.data)) {
90-
intern = Z_SXEOBJ_P(&sxe->iter.data);
91-
GET_NODE(intern, retnode)
92-
}
93-
return retnode;
94-
} else {
95-
return node;
96-
}
97-
}
98-
/* }}} */
99-
10081
static xmlNodePtr php_sxe_get_first_node_non_destructive(php_sxe_object *sxe, xmlNodePtr node)
10182
{
10283
if (sxe && sxe->iter.type != SXE_ITER_NONE) {
@@ -184,7 +165,7 @@ static xmlNodePtr sxe_get_element_by_name(php_sxe_object *sxe, xmlNodePtr node,
184165
if (sxe->iter.type == SXE_ITER_NONE) {
185166
sxe->iter.type = SXE_ITER_CHILD;
186167
}
187-
node = php_sxe_get_first_node(sxe, node);
168+
node = php_sxe_get_first_node_non_destructive(sxe, node);
188169
sxe->iter.type = orgtype;
189170
}
190171

@@ -270,11 +251,11 @@ static zval *sxe_prop_dim_read(zend_object *object, zval *member, bool elements,
270251
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
271252
attribs = 1;
272253
elements = 0;
273-
node = php_sxe_get_first_node(sxe, node);
254+
node = php_sxe_get_first_node_non_destructive(sxe, node);
274255
attr = (xmlAttrPtr)node;
275256
test = sxe->iter.name != NULL;
276257
} else if (sxe->iter.type != SXE_ITER_CHILD) {
277-
node = php_sxe_get_first_node(sxe, node);
258+
node = php_sxe_get_first_node_non_destructive(sxe, node);
278259
attr = node ? node->properties : NULL;
279260
test = 0;
280261
if (!member && node && node->parent &&
@@ -322,7 +303,7 @@ static zval *sxe_prop_dim_read(zend_object *object, zval *member, bool elements,
322303
xmlNodePtr mynode = node;
323304

324305
if (sxe->iter.type == SXE_ITER_CHILD) {
325-
node = php_sxe_get_first_node(sxe, node);
306+
node = php_sxe_get_first_node_non_destructive(sxe, node);
326307
}
327308
if (sxe->iter.type == SXE_ITER_NONE) {
328309
if (member && Z_LVAL_P(member) > 0) {
@@ -456,12 +437,12 @@ static zval *sxe_prop_dim_write(zend_object *object, zval *member, zval *value,
456437
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
457438
attribs = 1;
458439
elements = 0;
459-
node = php_sxe_get_first_node(sxe, node);
440+
node = php_sxe_get_first_node_non_destructive(sxe, node);
460441
attr = (xmlAttrPtr)node;
461442
test = sxe->iter.name != NULL;
462443
} else if (sxe->iter.type != SXE_ITER_CHILD) {
463444
mynode = node;
464-
node = php_sxe_get_first_node(sxe, node);
445+
node = php_sxe_get_first_node_non_destructive(sxe, node);
465446
attr = node ? node->properties : NULL;
466447
test = 0;
467448
if (!member && node && node->parent &&
@@ -707,19 +688,19 @@ static int sxe_prop_dim_exists(zend_object *object, zval *member, int check_empt
707688
attribs = 0;
708689
elements = 1;
709690
if (sxe->iter.type == SXE_ITER_CHILD) {
710-
node = php_sxe_get_first_node(sxe, node);
691+
node = php_sxe_get_first_node_non_destructive(sxe, node);
711692
}
712693
}
713694
}
714695

715696
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
716697
attribs = 1;
717698
elements = 0;
718-
node = php_sxe_get_first_node(sxe, node);
699+
node = php_sxe_get_first_node_non_destructive(sxe, node);
719700
attr = (xmlAttrPtr)node;
720701
test = sxe->iter.name != NULL;
721702
} else if (sxe->iter.type != SXE_ITER_CHILD) {
722-
node = php_sxe_get_first_node(sxe, node);
703+
node = php_sxe_get_first_node_non_destructive(sxe, node);
723704
attr = node ? node->properties : NULL;
724705
test = 0;
725706
}
@@ -759,7 +740,7 @@ static int sxe_prop_dim_exists(zend_object *object, zval *member, int check_empt
759740
if (elements) {
760741
if (Z_TYPE_P(member) == IS_LONG) {
761742
if (sxe->iter.type == SXE_ITER_CHILD) {
762-
node = php_sxe_get_first_node(sxe, node);
743+
node = php_sxe_get_first_node_non_destructive(sxe, node);
763744
}
764745
node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL);
765746
} else {
@@ -829,19 +810,19 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements
829810
attribs = 0;
830811
elements = 1;
831812
if (sxe->iter.type == SXE_ITER_CHILD) {
832-
node = php_sxe_get_first_node(sxe, node);
813+
node = php_sxe_get_first_node_non_destructive(sxe, node);
833814
}
834815
}
835816
}
836817

837818
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
838819
attribs = 1;
839820
elements = 0;
840-
node = php_sxe_get_first_node(sxe, node);
821+
node = php_sxe_get_first_node_non_destructive(sxe, node);
841822
attr = (xmlAttrPtr)node;
842823
test = sxe->iter.name != NULL;
843824
} else if (sxe->iter.type != SXE_ITER_CHILD) {
844-
node = php_sxe_get_first_node(sxe, node);
825+
node = php_sxe_get_first_node_non_destructive(sxe, node);
845826
attr = node ? node->properties : NULL;
846827
test = 0;
847828
}
@@ -878,7 +859,7 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements
878859
if (elements) {
879860
if (Z_TYPE_P(member) == IS_LONG) {
880861
if (sxe->iter.type == SXE_ITER_CHILD) {
881-
node = php_sxe_get_first_node(sxe, node);
862+
node = php_sxe_get_first_node_non_destructive(sxe, node);
882863
}
883864
node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL);
884865
if (node) {
@@ -1011,7 +992,7 @@ static int sxe_prop_is_empty(zend_object *object) /* {{{ */
1011992
}
1012993

1013994
if (sxe->iter.type == SXE_ITER_ELEMENT) {
1014-
node = php_sxe_get_first_node(sxe, node);
995+
node = php_sxe_get_first_node_non_destructive(sxe, node);
1015996
}
1016997
if (!node || node->type != XML_ENTITY_DECL) {
1017998
attr = node ? (xmlAttrPtr)node->properties : NULL;
@@ -1025,7 +1006,7 @@ static int sxe_prop_is_empty(zend_object *object) /* {{{ */
10251006
}
10261007

10271008
GET_NODE(sxe, node);
1028-
node = php_sxe_get_first_node(sxe, node);
1009+
node = php_sxe_get_first_node_non_destructive(sxe, node);
10291010
is_empty = 1;
10301011
ZVAL_UNDEF(&iter_data);
10311012
if (node && sxe->iter.type != SXE_ITER_ATTRLIST) {
@@ -1120,7 +1101,7 @@ static HashTable *sxe_get_prop_hash(zend_object *object, int is_debug) /* {{{ */
11201101
}
11211102
if (is_debug || sxe->iter.type != SXE_ITER_CHILD) {
11221103
if (sxe->iter.type == SXE_ITER_ELEMENT) {
1123-
node = php_sxe_get_first_node(sxe, node);
1104+
node = php_sxe_get_first_node_non_destructive(sxe, node);
11241105
}
11251106
if (!node || node->type != XML_ENTITY_DECL) {
11261107
attr = node ? (xmlAttrPtr)node->properties : NULL;
@@ -1142,7 +1123,7 @@ static HashTable *sxe_get_prop_hash(zend_object *object, int is_debug) /* {{{ */
11421123
}
11431124

11441125
GET_NODE(sxe, node);
1145-
node = php_sxe_get_first_node(sxe, node);
1126+
node = php_sxe_get_first_node_non_destructive(sxe, node);
11461127

11471128
if (node && sxe->iter.type != SXE_ITER_ATTRLIST) {
11481129
if (node->type == XML_ATTRIBUTE_NODE) {
@@ -1301,7 +1282,7 @@ PHP_METHOD(SimpleXMLElement, xpath)
13011282
}
13021283

13031284
GET_NODE(sxe, nodeptr);
1304-
nodeptr = php_sxe_get_first_node(sxe, nodeptr);
1285+
nodeptr = php_sxe_get_first_node_non_destructive(sxe, nodeptr);
13051286
if (!nodeptr) {
13061287
return;
13071288
}
@@ -1410,7 +1391,7 @@ PHP_METHOD(SimpleXMLElement, asXML)
14101391

14111392
sxe = Z_SXEOBJ_P(ZEND_THIS);
14121393
GET_NODE(sxe, node);
1413-
node = php_sxe_get_first_node(sxe, node);
1394+
node = php_sxe_get_first_node_non_destructive(sxe, node);
14141395

14151396
if (!node) {
14161397
RETURN_FALSE;
@@ -1533,7 +1514,7 @@ PHP_METHOD(SimpleXMLElement, getNamespaces)
15331514

15341515
sxe = Z_SXEOBJ_P(ZEND_THIS);
15351516
GET_NODE(sxe, node);
1536-
node = php_sxe_get_first_node(sxe, node);
1517+
node = php_sxe_get_first_node_non_destructive(sxe, node);
15371518

15381519
if (node) {
15391520
if (node->type == XML_ELEMENT_NODE) {
@@ -1618,7 +1599,7 @@ PHP_METHOD(SimpleXMLElement, children)
16181599
}
16191600

16201601
GET_NODE(sxe, node);
1621-
node = php_sxe_get_first_node(sxe, node);
1602+
node = php_sxe_get_first_node_non_destructive(sxe, node);
16221603
if (!node) {
16231604
return;
16241605
}
@@ -1667,7 +1648,7 @@ PHP_METHOD(SimpleXMLElement, attributes)
16671648

16681649
sxe = Z_SXEOBJ_P(ZEND_THIS);
16691650
GET_NODE(sxe, node);
1670-
node = php_sxe_get_first_node(sxe, node);
1651+
node = php_sxe_get_first_node_non_destructive(sxe, node);
16711652
if (!node) {
16721653
return;
16731654
}
@@ -1708,7 +1689,7 @@ PHP_METHOD(SimpleXMLElement, addChild)
17081689
return;
17091690
}
17101691

1711-
node = php_sxe_get_first_node(sxe, node);
1692+
node = php_sxe_get_first_node_non_destructive(sxe, node);
17121693

17131694
if (node == NULL) {
17141695
php_error_docref(NULL, E_WARNING, "Cannot add child. Parent is not a permanent member of the XML tree");
@@ -1768,7 +1749,7 @@ PHP_METHOD(SimpleXMLElement, addAttribute)
17681749
sxe = Z_SXEOBJ_P(ZEND_THIS);
17691750
GET_NODE(sxe, node);
17701751

1771-
node = php_sxe_get_first_node(sxe, node);
1752+
node = php_sxe_get_first_node_non_destructive(sxe, node);
17721753

17731754
if (node && node->type != XML_ELEMENT_NODE) {
17741755
node = node->parent;
@@ -2615,7 +2596,7 @@ void *simplexml_export_node(zval *object) /* {{{ */
26152596

26162597
sxe = Z_SXEOBJ_P(object);
26172598
GET_NODE(sxe, node);
2618-
return php_sxe_get_first_node(sxe, node);
2599+
return php_sxe_get_first_node_non_destructive(sxe, node);
26192600
}
26202601
/* }}} */
26212602

‎ext/simplexml/tests/bug55098.phpt

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
--TEST--
2+
Bug #55098 (SimpleXML iteration produces infinite loop)
3+
--EXTENSIONS--
4+
simplexml
5+
--FILE--
6+
<?php
7+
$xmlString = "<root><a><b>1</b><b>2</b><b>3</b></a></root>";
8+
$xml = simplexml_load_string($xmlString);
9+
10+
$nodes = $xml->a->b;
11+
12+
function test($nodes, $name, $callable) {
13+
echo "--- $name ---\n";
14+
foreach ($nodes as $nodeData) {
15+
echo "nodeData: " . $nodeData . "\n";
16+
$callable($nodes);
17+
}
18+
}
19+
20+
test($nodes, "asXml", fn ($n) => $n->asXml());
21+
test($nodes, "attributes", fn ($n) => $n->attributes());
22+
test($nodes, "children", fn ($n) => $n->children());
23+
test($nodes, "getNamespaces", fn ($n) => $n->getNamespaces());
24+
test($nodes, "xpath", fn ($n) => $n->xpath("/root/a/b"));
25+
test($nodes, "var_dump", fn ($n) => var_dump($n));
26+
test($nodes, "manipulation combined with querying", function ($n) {
27+
$n->addAttribute("attr", "value");
28+
(bool) $n["attr"];
29+
$n->addChild("child", "value");
30+
$n->outer[]->inner = "foo";
31+
(bool) $n->outer;
32+
(bool) $n;
33+
isset($n->outer);
34+
isset($n["attr"]);
35+
unset($n->outer);
36+
unset($n["attr"]);
37+
unset($n->child);
38+
});
39+
?>
40+
--EXPECT--
41+
--- asXml ---
42+
nodeData: 1
43+
nodeData: 2
44+
nodeData: 3
45+
--- attributes ---
46+
nodeData: 1
47+
nodeData: 2
48+
nodeData: 3
49+
--- children ---
50+
nodeData: 1
51+
nodeData: 2
52+
nodeData: 3
53+
--- getNamespaces ---
54+
nodeData: 1
55+
nodeData: 2
56+
nodeData: 3
57+
--- xpath ---
58+
nodeData: 1
59+
nodeData: 2
60+
nodeData: 3
61+
--- var_dump ---
62+
nodeData: 1
63+
object(SimpleXMLElement)#3 (3) {
64+
[0]=>
65+
string(1) "1"
66+
[1]=>
67+
string(1) "2"
68+
[2]=>
69+
string(1) "3"
70+
}
71+
nodeData: 2
72+
object(SimpleXMLElement)#3 (3) {
73+
[0]=>
74+
string(1) "1"
75+
[1]=>
76+
string(1) "2"
77+
[2]=>
78+
string(1) "3"
79+
}
80+
nodeData: 3
81+
object(SimpleXMLElement)#3 (3) {
82+
[0]=>
83+
string(1) "1"
84+
[1]=>
85+
string(1) "2"
86+
[2]=>
87+
string(1) "3"
88+
}
89+
--- manipulation combined with querying ---
90+
nodeData: 1
91+
nodeData: 2
92+
nodeData: 3

‎ext/simplexml/tests/bug62639.phpt

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ foreach ($a2->b->c->children() as $key => $value) {
4141
var_dump($value);
4242
}?>
4343
--EXPECT--
44-
object(A)#2 (2) {
44+
object(A)#4 (2) {
4545
["@attributes"]=>
4646
array(1) {
4747
["attr"]=>
@@ -50,7 +50,7 @@ object(A)#2 (2) {
5050
[0]=>
5151
string(10) "Some Value"
5252
}
53-
object(A)#3 (2) {
53+
object(A)#6 (2) {
5454
["@attributes"]=>
5555
array(1) {
5656
["attr"]=>

0 commit comments

Comments
 (0)
Please sign in to comment.