diff --git a/NEWS b/NEWS
index 474db936ef15..9ce1698bce56 100644
--- a/NEWS
+++ b/NEWS
@@ -42,6 +42,9 @@ PHP NEWS
. Fixed bug GH-23043 (broken session id code can cause zend_mm_heap
corrupted). (ndossche)
+- SimpleXML:
+ . Fixed negative element offsets aliasing the first element. (iliaal)
+
- Sockets:
. Fixed various memory related issues in ext/sockets. (David Carlier)
diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c
index 8cf8e657e58f..2976885f6043 100644
--- a/ext/simplexml/simplexml.c
+++ b/ext/simplexml/simplexml.c
@@ -134,7 +134,7 @@ static xmlNodePtr sxe_get_element_by_offset(php_sxe_object *sxe, zend_long offse
return NULL;
}
}
- while (node && nodendx <= offset) {
+ while (node && (offset < 0 || nodendx <= offset)) {
if (node->type == XML_ELEMENT_NODE && match_ns(node, sxe->iter.nsprefix, sxe->iter.isprefix)) {
if (sxe->iter.type == SXE_ITER_CHILD || (
sxe->iter.type == SXE_ITER_ELEMENT && xmlStrEqual(node->name, BAD_CAST ZSTR_VAL(sxe->iter.name)))) {
@@ -319,11 +319,13 @@ static zval *sxe_prop_dim_read(zend_object *object, zval *member, bool elements,
if (node) {
node_as_zval(sxe, node, rv, SXE_ITER_NONE, NULL, sxe->iter.nsprefix, sxe->iter.isprefix);
} else if (type == BP_VAR_W || type == BP_VAR_RW) {
- if (member && cnt < Z_LVAL_P(member)) {
+ if (member && (Z_LVAL_P(member) < 0 || cnt < Z_LVAL_P(member))) {
php_error_docref(NULL, E_WARNING, "Cannot add element %s number " ZEND_LONG_FMT " when only " ZEND_LONG_FMT " such elements exist", mynode->name, Z_LVAL_P(member), cnt);
}
- node = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name, NULL);
- node_as_zval(sxe, node, rv, SXE_ITER_NONE, NULL, sxe->iter.nsprefix, sxe->iter.isprefix);
+ if (!member || Z_LVAL_P(member) >= 0) {
+ node = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name, NULL);
+ node_as_zval(sxe, node, rv, SXE_ITER_NONE, NULL, sxe->iter.nsprefix, sxe->iter.isprefix);
+ }
}
} else {
/* In BP_VAR_IS mode only return a proper node if it actually exists. */
@@ -586,10 +588,14 @@ static zval *sxe_prop_dim_write(zend_object *object, zval *member, zval *value,
newnode = xmlNewTextChild(mynode, NULL, (xmlChar *)Z_STRVAL_P(member), value_str ? (xmlChar *)ZSTR_VAL(value_str) : NULL);
}
} else if (!member || Z_TYPE_P(member) == IS_LONG) {
- if (member && cnt < Z_LVAL_P(member)) {
+ if (member && (Z_LVAL_P(member) < 0 || cnt < Z_LVAL_P(member))) {
php_error_docref(NULL, E_WARNING, "Cannot add element %s number " ZEND_LONG_FMT " when only " ZEND_LONG_FMT " such elements exist", mynode->name, Z_LVAL_P(member), cnt);
}
- newnode = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name, value_str ? (xmlChar *)ZSTR_VAL(value_str) : NULL);
+ if (member && Z_LVAL_P(member) < 0) {
+ value = &EG(error_zval);
+ } else {
+ newnode = xmlNewTextChild(mynode->parent, mynode->ns, mynode->name, value_str ? (xmlChar *)ZSTR_VAL(value_str) : NULL);
+ }
}
} else if (attribs) {
if (Z_TYPE_P(member) == IS_LONG) {
diff --git a/ext/simplexml/tests/bug_sxe_negative_offset.phpt b/ext/simplexml/tests/bug_sxe_negative_offset.phpt
new file mode 100644
index 000000000000..5df3cb2548e2
--- /dev/null
+++ b/ext/simplexml/tests/bug_sxe_negative_offset.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Negative SimpleXML element offsets must not alias the first element
+--EXTENSIONS--
+simplexml
+--FILE--
+- a
- b
- c
');
+$items = $xml->item;
+
+echo "isset[-1]: ";
+var_dump(isset($items[-1]));
+echo "read[-1]: ";
+var_dump($items[-1]);
+echo "read[0]: ";
+var_dump((string)$items[0]);
+
+$items[-1] = 'Z';
+echo "after negative write: ", $xml->asXML();
+
+$items[5] = 'P';
+echo "after out-of-range write: ", $xml->asXML();
+?>
+--EXPECTF--
+isset[-1]: bool(false)
+read[-1]: NULL
+read[0]: string(1) "a"
+
+Warning: main(): Cannot add element item number -1 when only 3 such elements exist in %s on line %d
+after negative write:
+- a
- b
- c
+
+Warning: main(): Cannot add element item number 5 when only 3 such elements exist in %s on line %d
+after out-of-range write:
+- a
- b
- c
- P