diff --git a/ext/hash/hash.c b/ext/hash/hash.c
index 5818901a765..e0024daf90d 100644
--- a/ext/hash/hash.c
+++ b/ext/hash/hash.c
@@ -681,7 +681,7 @@ PHP_FUNCTION(hash_init)
#define PHP_HASHCONTEXT_VERIFY(hash) { \
if (!hash->context) { \
- zend_argument_type_error(1, "must be a valid Hash Context resource"); \
+ zend_argument_type_error(1, "must be a valid, non-finalized HashContext"); \
RETURN_THROWS(); \
} \
}
@@ -838,11 +838,15 @@ PHP_FUNCTION(hash_final)
PHP_FUNCTION(hash_copy)
{
zval *zhash;
+ php_hashcontext_object *context;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &zhash, php_hashcontext_ce) == FAILURE) {
RETURN_THROWS();
}
+ context = php_hashcontext_from_object(Z_OBJ_P(zhash));
+ PHP_HASHCONTEXT_VERIFY(context);
+
RETVAL_OBJ(Z_OBJ_HANDLER_P(zhash, clone_obj)(Z_OBJ_P(zhash)));
if (php_hashcontext_from_object(Z_OBJ_P(return_value))->context == NULL) {
@@ -1395,6 +1399,11 @@ static zend_object *php_hashcontext_clone(zend_object *zobj) {
zend_object *znew = php_hashcontext_create(zobj->ce);
php_hashcontext_object *newobj = php_hashcontext_from_object(znew);
+ if (!oldobj->context) {
+ zend_throw_exception(zend_ce_value_error, "Cannot clone a finalized HashContext", 0);
+ return znew;
+ }
+
zend_objects_clone_members(znew, zobj);
newobj->ops = oldobj->ops;
diff --git a/ext/hash/tests/gh12186_1.phpt b/ext/hash/tests/gh12186_1.phpt
new file mode 100644
index 00000000000..5e34b1dd78e
--- /dev/null
+++ b/ext/hash/tests/gh12186_1.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Hash: bug #12186 - segfault in hash_copy() on a finalized context
+--FILE--
+getMessage() . "\n";
+}
+
+?>
+--EXPECTF--
+hash_copy(): Argument #1 ($context) must be a valid, non-finalized HashContext
diff --git a/ext/hash/tests/gh12186_2.phpt b/ext/hash/tests/gh12186_2.phpt
new file mode 100644
index 00000000000..64a12b15c39
--- /dev/null
+++ b/ext/hash/tests/gh12186_2.phpt
@@ -0,0 +1,17 @@
+--TEST--
+Hash: bug #12186 - segfault when cloning a finalized context
+--FILE--
+getMessage() . "\n";
+}
+
+?>
+--EXPECTF--
+Cannot clone a finalized HashContext
diff --git a/ext/hash/tests/reuse.phpt b/ext/hash/tests/reuse.phpt
index 229236dd713..b5cfc79c162 100644
--- a/ext/hash/tests/reuse.phpt
+++ b/ext/hash/tests/reuse.phpt
@@ -14,4 +14,4 @@ catch (\Error $e) {
?>
--EXPECT--
-hash_update(): Argument #1 ($context) must be a valid Hash Context resource
+hash_update(): Argument #1 ($context) must be a valid, non-finalized HashContext
diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c
index 160d4eaa457..140773766a2 100644
--- a/ext/simplexml/simplexml.c
+++ b/ext/simplexml/simplexml.c
@@ -44,6 +44,7 @@ PHP_SXE_API zend_class_entry *sxe_get_element_class_entry(void) /* {{{ */
static php_sxe_object* php_sxe_object_new(zend_class_entry *ce, zend_function *fptr_count);
static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data);
+static xmlNodePtr php_sxe_reset_iterator_no_clear_iter_data(php_sxe_object *sxe, int use_data);
static xmlNodePtr php_sxe_iterator_fetch(php_sxe_object *sxe, xmlNodePtr node, int use_data);
static void php_sxe_iterator_dtor(zend_object_iterator *iter);
static int php_sxe_iterator_valid(zend_object_iterator *iter);
@@ -76,6 +77,7 @@ static void _node_as_zval(php_sxe_object *sxe, xmlNodePtr node, zval *value, SXE
}
/* }}} */
+/* Important: this overwrites the iterator data, if you wish to keep it use php_sxe_get_first_node_non_destructive() instead! */
static xmlNodePtr php_sxe_get_first_node(php_sxe_object *sxe, xmlNodePtr node) /* {{{ */
{
if (sxe && sxe->iter.type != SXE_ITER_NONE) {
@@ -86,6 +88,15 @@ static xmlNodePtr php_sxe_get_first_node(php_sxe_object *sxe, xmlNodePtr node) /
}
/* }}} */
+static xmlNodePtr php_sxe_get_first_node_non_destructive(php_sxe_object *sxe, xmlNodePtr node)
+{
+ if (sxe && sxe->iter.type != SXE_ITER_NONE) {
+ return php_sxe_reset_iterator_no_clear_iter_data(sxe, false);
+ } else {
+ return node;
+ }
+}
+
static inline int match_ns(php_sxe_object *sxe, xmlNodePtr node, xmlChar *name, int prefix) /* {{{ */
{
if (name == NULL && (node->ns == NULL || node->ns->prefix == NULL)) {
@@ -1163,6 +1174,12 @@ static HashTable *sxe_get_prop_hash(zend_object *object, int is_debug) /* {{{ */
sxe_properties_add(rv, name, namelen, &value);
}
next_iter:
+ if (UNEXPECTED(node->type == XML_ENTITY_DECL)) {
+ /* Entity decls are linked together via the next pointer.
+ * The only way to get to an entity decl is via an entity reference in the document.
+ * If we then continue iterating, we'll end up in the DTD. Even worse, if the entities reference each other we'll infinite loop. */
+ break;
+ }
if (use_iter) {
node = php_sxe_iterator_fetch(sxe, node->next, 0);
} else {
@@ -1598,7 +1615,7 @@ PHP_METHOD(SimpleXMLElement, getName)
sxe = Z_SXEOBJ_P(ZEND_THIS);
GET_NODE(sxe, node);
- node = php_sxe_get_first_node(sxe, node);
+ node = php_sxe_get_first_node_non_destructive(sxe, node);
if (node) {
namelen = xmlStrlen(node->name);
RETURN_STRINGL((char*)node->name, namelen);
@@ -2425,15 +2442,9 @@ static xmlNodePtr php_sxe_iterator_fetch(php_sxe_object *sxe, xmlNodePtr node, i
}
/* }}} */
-static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data) /* {{{ */
+static xmlNodePtr php_sxe_reset_iterator_no_clear_iter_data(php_sxe_object *sxe, int use_data)
{
xmlNodePtr node;
-
- if (!Z_ISUNDEF(sxe->iter.data)) {
- zval_ptr_dtor(&sxe->iter.data);
- ZVAL_UNDEF(&sxe->iter.data);
- }
-
GET_NODE(sxe, node)
if (node) {
@@ -2446,10 +2457,23 @@ static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data) /* {
case SXE_ITER_ATTRLIST:
node = (xmlNodePtr) node->properties;
}
+ if (use_data) {
+ ZEND_ASSERT(Z_ISUNDEF(sxe->iter.data));
+ }
return php_sxe_iterator_fetch(sxe, node, use_data);
}
return NULL;
}
+
+static xmlNodePtr php_sxe_reset_iterator(php_sxe_object *sxe, int use_data) /* {{{ */
+{
+ if (!Z_ISUNDEF(sxe->iter.data)) {
+ zval_ptr_dtor(&sxe->iter.data);
+ ZVAL_UNDEF(&sxe->iter.data);
+ }
+
+ return php_sxe_reset_iterator_no_clear_iter_data(sxe, use_data);
+}
/* }}} */
zend_object_iterator *php_sxe_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */
diff --git a/ext/simplexml/tests/gh12192.phpt b/ext/simplexml/tests/gh12192.phpt
new file mode 100644
index 00000000000..4d648495a10
--- /dev/null
+++ b/ext/simplexml/tests/gh12192.phpt
@@ -0,0 +1,37 @@
+--TEST--
+GH-12192 (SimpleXML infinite loop when getName() is called within foreach)
+--EXTENSIONS--
+simplexml
+--FILE--
+12";
+$xml = simplexml_load_string($xml);
+
+$a = $xml->a;
+
+foreach ($a as $test) {
+ echo "Iteration\n";
+ var_dump($a->key());
+ var_dump($a->getName());
+ var_dump((string) $test);
+}
+
+var_dump($a);
+
+?>
+--EXPECT--
+Iteration
+string(1) "a"
+string(1) "a"
+string(1) "1"
+Iteration
+string(1) "a"
+string(1) "a"
+string(1) "2"
+object(SimpleXMLElement)#2 (2) {
+ [0]=>
+ string(1) "1"
+ [1]=>
+ string(1) "2"
+}
diff --git a/ext/simplexml/tests/gh12223.phpt b/ext/simplexml/tests/gh12223.phpt
new file mode 100644
index 00000000000..0be61dec2ff
--- /dev/null
+++ b/ext/simplexml/tests/gh12223.phpt
@@ -0,0 +1,67 @@
+--TEST--
+GH-12223: Entity reference produces infinite loop in var_dump/print_r
+--EXTENSIONS--
+simplexml
+--FILE--
+
+
+
+
+]>
+&c;
+XML;
+
+$sxe = simplexml_load_string($xml);
+
+var_dump($sxe);
+print_r($sxe);
+
+?>
+--EXPECT--
+object(SimpleXMLElement)#1 (1) {
+ ["c"]=>
+ object(SimpleXMLElement)#2 (1) {
+ ["c"]=>
+ object(SimpleXMLElement)#3 (1) {
+ ["b"]=>
+ object(SimpleXMLElement)#4 (1) {
+ ["b"]=>
+ object(SimpleXMLElement)#5 (1) {
+ ["a"]=>
+ object(SimpleXMLElement)#6 (1) {
+ ["a"]=>
+ string(9) "something"
+ }
+ }
+ }
+ }
+ }
+}
+SimpleXMLElement Object
+(
+ [c] => SimpleXMLElement Object
+ (
+ [c] => SimpleXMLElement Object
+ (
+ [b] => SimpleXMLElement Object
+ (
+ [b] => SimpleXMLElement Object
+ (
+ [a] => SimpleXMLElement Object
+ (
+ [a] => something
+ )
+
+ )
+
+ )
+
+ )
+
+ )
+
+)