Skip to content

Commit b08802d

Browse files
committed
Merge branch 'PHP-8.2' into PHP-8.3
* PHP-8.2: Fix GH-12215: Module entry being overwritten causes type errors in ext/dom (<= PHP 8.3) Fix bug #55098: SimpleXML iteration produces infinite loop
2 parents 1bed209 + 84fae4a commit b08802d

File tree

5 files changed

+139
-48
lines changed

5 files changed

+139
-48
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ PHP NEWS
77
parent class implementations). (timwolla)
88
. Fixed OSS Fuzz #62294 (Unsetting variable after ++/-- on string variable
99
warning). (Girgias)
10+
. Fixed bug GH-12215 (Module entry being overwritten causes type errors in
11+
ext/dom). (nielsdos)
1012

1113
- Core:
1214
. Fixed bug GH-12207 (memory leak when class using trait with doc block).
@@ -27,6 +29,7 @@ PHP NEWS
2729
var_dump/print_r). (nielsdos)
2830
. Fixed bug GH-12208 (SimpleXML infinite loop when a cast is used inside a
2931
foreach). (nielsdos)
32+
. Fixed bug #55098 (SimpleXML iteration produces infinite loop). (nielsdos)
3033

3134
14 Sep 2023, PHP 8.3.0RC2
3235

ext/simplexml/simplexml.c

Lines changed: 26 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -77,25 +77,6 @@ static void _node_as_zval(php_sxe_object *sxe, xmlNodePtr node, zval *value, SXE
7777
}
7878
/* }}} */
7979

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

@@ -257,11 +238,11 @@ static zval *sxe_prop_dim_read(zend_object *object, zval *member, bool elements,
257238
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
258239
attribs = 1;
259240
elements = 0;
260-
node = php_sxe_get_first_node(sxe, node);
241+
node = php_sxe_get_first_node_non_destructive(sxe, node);
261242
attr = (xmlAttrPtr)node;
262243
test = sxe->iter.name != NULL;
263244
} else if (sxe->iter.type != SXE_ITER_CHILD) {
264-
node = php_sxe_get_first_node(sxe, node);
245+
node = php_sxe_get_first_node_non_destructive(sxe, node);
265246
attr = node ? node->properties : NULL;
266247
test = 0;
267248
if (!member && node && node->parent &&
@@ -309,7 +290,7 @@ static zval *sxe_prop_dim_read(zend_object *object, zval *member, bool elements,
309290
xmlNodePtr mynode = node;
310291

311292
if (sxe->iter.type == SXE_ITER_CHILD) {
312-
node = php_sxe_get_first_node(sxe, node);
293+
node = php_sxe_get_first_node_non_destructive(sxe, node);
313294
}
314295
if (sxe->iter.type == SXE_ITER_NONE) {
315296
if (member && Z_LVAL_P(member) > 0) {
@@ -445,12 +426,12 @@ static zval *sxe_prop_dim_write(zend_object *object, zval *member, zval *value,
445426
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
446427
attribs = 1;
447428
elements = 0;
448-
node = php_sxe_get_first_node(sxe, node);
429+
node = php_sxe_get_first_node_non_destructive(sxe, node);
449430
attr = (xmlAttrPtr)node;
450431
test = sxe->iter.name != NULL;
451432
} else if (sxe->iter.type != SXE_ITER_CHILD) {
452433
mynode = node;
453-
node = php_sxe_get_first_node(sxe, node);
434+
node = php_sxe_get_first_node_non_destructive(sxe, node);
454435
attr = node ? node->properties : NULL;
455436
test = 0;
456437
if (!member && node && node->parent &&
@@ -696,19 +677,19 @@ static int sxe_prop_dim_exists(zend_object *object, zval *member, int check_empt
696677
attribs = 0;
697678
elements = 1;
698679
if (sxe->iter.type == SXE_ITER_CHILD) {
699-
node = php_sxe_get_first_node(sxe, node);
680+
node = php_sxe_get_first_node_non_destructive(sxe, node);
700681
}
701682
}
702683
}
703684

704685
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
705686
attribs = 1;
706687
elements = 0;
707-
node = php_sxe_get_first_node(sxe, node);
688+
node = php_sxe_get_first_node_non_destructive(sxe, node);
708689
attr = (xmlAttrPtr)node;
709690
test = sxe->iter.name != NULL;
710691
} else if (sxe->iter.type != SXE_ITER_CHILD) {
711-
node = php_sxe_get_first_node(sxe, node);
692+
node = php_sxe_get_first_node_non_destructive(sxe, node);
712693
attr = node ? node->properties : NULL;
713694
test = 0;
714695
}
@@ -748,7 +729,7 @@ static int sxe_prop_dim_exists(zend_object *object, zval *member, int check_empt
748729
if (elements) {
749730
if (Z_TYPE_P(member) == IS_LONG) {
750731
if (sxe->iter.type == SXE_ITER_CHILD) {
751-
node = php_sxe_get_first_node(sxe, node);
732+
node = php_sxe_get_first_node_non_destructive(sxe, node);
752733
}
753734
node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL);
754735
} else {
@@ -820,19 +801,19 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements
820801
attribs = 0;
821802
elements = 1;
822803
if (sxe->iter.type == SXE_ITER_CHILD) {
823-
node = php_sxe_get_first_node(sxe, node);
804+
node = php_sxe_get_first_node_non_destructive(sxe, node);
824805
}
825806
}
826807
}
827808

828809
if (sxe->iter.type == SXE_ITER_ATTRLIST) {
829810
attribs = 1;
830811
elements = 0;
831-
node = php_sxe_get_first_node(sxe, node);
812+
node = php_sxe_get_first_node_non_destructive(sxe, node);
832813
attr = (xmlAttrPtr)node;
833814
test = sxe->iter.name != NULL;
834815
} else if (sxe->iter.type != SXE_ITER_CHILD) {
835-
node = php_sxe_get_first_node(sxe, node);
816+
node = php_sxe_get_first_node_non_destructive(sxe, node);
836817
attr = node ? node->properties : NULL;
837818
test = 0;
838819
}
@@ -869,7 +850,7 @@ static void sxe_prop_dim_delete(zend_object *object, zval *member, bool elements
869850
if (elements) {
870851
if (Z_TYPE_P(member) == IS_LONG) {
871852
if (sxe->iter.type == SXE_ITER_CHILD) {
872-
node = php_sxe_get_first_node(sxe, node);
853+
node = php_sxe_get_first_node_non_destructive(sxe, node);
873854
}
874855
node = sxe_get_element_by_offset(sxe, Z_LVAL_P(member), node, NULL);
875856
if (node) {
@@ -1002,7 +983,7 @@ static int sxe_prop_is_empty(zend_object *object) /* {{{ */
1002983
}
1003984

1004985
if (sxe->iter.type == SXE_ITER_ELEMENT) {
1005-
node = php_sxe_get_first_node(sxe, node);
986+
node = php_sxe_get_first_node_non_destructive(sxe, node);
1006987
}
1007988
if (!node || node->type != XML_ENTITY_DECL) {
1008989
attr = node ? (xmlAttrPtr)node->properties : NULL;
@@ -1016,7 +997,7 @@ static int sxe_prop_is_empty(zend_object *object) /* {{{ */
1016997
}
1017998

1018999
GET_NODE(sxe, node);
1019-
node = php_sxe_get_first_node(sxe, node);
1000+
node = php_sxe_get_first_node_non_destructive(sxe, node);
10201001
is_empty = 1;
10211002
ZVAL_UNDEF(&iter_data);
10221003
if (node && sxe->iter.type != SXE_ITER_ATTRLIST) {
@@ -1111,7 +1092,7 @@ static HashTable *sxe_get_prop_hash(zend_object *object, int is_debug) /* {{{ */
11111092
}
11121093
if (is_debug || sxe->iter.type != SXE_ITER_CHILD) {
11131094
if (sxe->iter.type == SXE_ITER_ELEMENT) {
1114-
node = php_sxe_get_first_node(sxe, node);
1095+
node = php_sxe_get_first_node_non_destructive(sxe, node);
11151096
}
11161097
if (!node || node->type != XML_ENTITY_DECL) {
11171098
attr = node ? (xmlAttrPtr)node->properties : NULL;
@@ -1133,7 +1114,7 @@ static HashTable *sxe_get_prop_hash(zend_object *object, int is_debug) /* {{{ */
11331114
}
11341115

11351116
GET_NODE(sxe, node);
1136-
node = php_sxe_get_first_node(sxe, node);
1117+
node = php_sxe_get_first_node_non_destructive(sxe, node);
11371118

11381119
if (node && sxe->iter.type != SXE_ITER_ATTRLIST) {
11391120
if (node->type == XML_ATTRIBUTE_NODE) {
@@ -1292,7 +1273,7 @@ PHP_METHOD(SimpleXMLElement, xpath)
12921273
}
12931274

12941275
GET_NODE(sxe, nodeptr);
1295-
nodeptr = php_sxe_get_first_node(sxe, nodeptr);
1276+
nodeptr = php_sxe_get_first_node_non_destructive(sxe, nodeptr);
12961277
if (!nodeptr) {
12971278
return;
12981279
}
@@ -1401,7 +1382,7 @@ PHP_METHOD(SimpleXMLElement, asXML)
14011382

14021383
sxe = Z_SXEOBJ_P(ZEND_THIS);
14031384
GET_NODE(sxe, node);
1404-
node = php_sxe_get_first_node(sxe, node);
1385+
node = php_sxe_get_first_node_non_destructive(sxe, node);
14051386

14061387
if (!node) {
14071388
RETURN_FALSE;
@@ -1524,7 +1505,7 @@ PHP_METHOD(SimpleXMLElement, getNamespaces)
15241505

15251506
sxe = Z_SXEOBJ_P(ZEND_THIS);
15261507
GET_NODE(sxe, node);
1527-
node = php_sxe_get_first_node(sxe, node);
1508+
node = php_sxe_get_first_node_non_destructive(sxe, node);
15281509

15291510
if (node) {
15301511
if (node->type == XML_ELEMENT_NODE) {
@@ -1609,7 +1590,7 @@ PHP_METHOD(SimpleXMLElement, children)
16091590
}
16101591

16111592
GET_NODE(sxe, node);
1612-
node = php_sxe_get_first_node(sxe, node);
1593+
node = php_sxe_get_first_node_non_destructive(sxe, node);
16131594
if (!node) {
16141595
return;
16151596
}
@@ -1658,7 +1639,7 @@ PHP_METHOD(SimpleXMLElement, attributes)
16581639

16591640
sxe = Z_SXEOBJ_P(ZEND_THIS);
16601641
GET_NODE(sxe, node);
1661-
node = php_sxe_get_first_node(sxe, node);
1642+
node = php_sxe_get_first_node_non_destructive(sxe, node);
16621643
if (!node) {
16631644
return;
16641645
}
@@ -1701,7 +1682,7 @@ PHP_METHOD(SimpleXMLElement, addChild)
17011682
return;
17021683
}
17031684

1704-
node = php_sxe_get_first_node(sxe, node);
1685+
node = php_sxe_get_first_node_non_destructive(sxe, node);
17051686

17061687
if (node == NULL) {
17071688
php_error_docref(NULL, E_WARNING, "Cannot add child. Parent is not a permanent member of the XML tree");
@@ -1761,7 +1742,7 @@ PHP_METHOD(SimpleXMLElement, addAttribute)
17611742
sxe = Z_SXEOBJ_P(ZEND_THIS);
17621743
GET_NODE(sxe, node);
17631744

1764-
node = php_sxe_get_first_node(sxe, node);
1745+
node = php_sxe_get_first_node_non_destructive(sxe, node);
17651746

17661747
if (node && node->type != XML_ELEMENT_NODE) {
17671748
node = node->parent;
@@ -2608,7 +2589,7 @@ void *simplexml_export_node(zval *object) /* {{{ */
26082589

26092590
sxe = Z_SXEOBJ_P(object);
26102591
GET_NODE(sxe, node);
2611-
return php_sxe_get_first_node(sxe, node);
2592+
return php_sxe_get_first_node_non_destructive(sxe, node);
26122593
}
26132594
/* }}} */
26142595

ext/simplexml/tests/bug55098.phpt

Lines changed: 92 additions & 0 deletions
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

Lines changed: 2 additions & 2 deletions
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)