diff --git a/Zend/tests/gh18519.phpt b/Zend/tests/gh18519.phpt new file mode 100644 index 0000000000000..24881401b0615 --- /dev/null +++ b/Zend/tests/gh18519.phpt @@ -0,0 +1,39 @@ +--TEST-- +GH-18519: Nested object comparison leading to stack overflow +--SKIPIF-- + +--FILE-- +previous = $first; +$first->next = $first; + +$cur = $first; + +for ($i = 0; $i < 50000; $i++) { + $new = new Node(); + $new->previous = $cur; + $cur->next = $new; + $new->next = $first; + $first->previous = $new; + $cur = $new; +} + +try { + // Force comparison manually to trigger zend_hash_compare + $first == $cur; +} catch(Error $e) { + echo $e->getMessage(). PHP_EOL; +} +?> +--EXPECTREGEX-- +(Maximum call stack size reached during object comparison|Fatal error: Nesting level too deep - recursive dependency?.+) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index d688e4b63ed69..180364b248d71 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -42,6 +42,15 @@ #define IN_UNSET ZEND_GUARD_PROPERTY_UNSET #define IN_ISSET ZEND_GUARD_PROPERTY_ISSET +static zend_always_inline bool zend_objects_check_stack_limit(void) +{ +#ifdef ZEND_CHECK_STACK_LIMIT + return zend_call_stack_overflowed(EG(stack_limit)); +#else + return false; +#endif +} + /* __X accessors explanation: @@ -1714,6 +1723,11 @@ ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */ { zend_object *zobj1, *zobj2; + if (zend_objects_check_stack_limit()) { + zend_throw_error(NULL, "Maximum call stack size reached during object comparison"); + return ZEND_UNCOMPARABLE; + } + if (Z_TYPE_P(o1) != Z_TYPE_P(o2)) { /* Object and non-object */ zval *object;