Description
Follow-up to #737, which ships only the regression test (ignored) and docs.
Any #[php(prop)] field holding an owned refcounted type leaks one zend_string per call when read through Exception::getMessage (or any C method using zval_get_string + RETURN_STR). The generated getter writes a refcount=1 string into the rv slot, getMessage reads it and addrefs to 2, transfers the pointer to return_value, then returns without zval_ptr_dtor(&rv). One refcount orphaned per call. Direct property access via FETCH_OBJ_R is not affected.
Two viable fixes:
- Mirror the shadow field into the parent's real property slot via
zend_update_property_stringl at write time, so reads go through zend_std_read_property and return a direct pointer. Self-contained in ext-php-rs.
- Upstream PHP patch so
Exception::getMessage and siblings call zval_ptr_dtor(&rv) when retval == &rv. (not sure)
Steps to Reproduce
cargo test -p tests -- --ignored prop_string_field_does_not_leak_on_repeated_get_message
- Without the fix the test fails: roughly 75KB heap growth on release, hashtable refcount assertion on debug.
Example
Extension Code:
#[php_class]
#[php(extends(ce = ce::exception, stub = "\\Exception"))]
#[derive(Default)]
pub struct MyException {
#[php(prop)]
pub message: String,
}
PHP Code:
<?php
try { throw_my_exception(); } catch (MyException $e) {
for ($i = 0; $i < 500; $i++) $e->getMessage();
}
Actual Behavior
One zend_string orphaned per getMessage() call. Linear memory growth.
Expected Behavior
Stable memory across repeated reads.
PHP Version
PHP 8.5.2-dev (cli) (NTS DEBUG)
ext-php-rs Version
master + PR #737
Operating System
Linux
Description
Follow-up to #737, which ships only the regression test (ignored) and docs.
Any
#[php(prop)]field holding an owned refcounted type leaks onezend_stringper call when read throughException::getMessage(or any C method usingzval_get_string + RETURN_STR). The generated getter writes a refcount=1 string into thervslot,getMessagereads it and addrefs to 2, transfers the pointer toreturn_value, then returns withoutzval_ptr_dtor(&rv). One refcount orphaned per call. Direct property access viaFETCH_OBJ_Ris not affected.Two viable fixes:
zend_update_property_stringlat write time, so reads go throughzend_std_read_propertyand return a direct pointer. Self-contained in ext-php-rs.Exception::getMessageand siblings callzval_ptr_dtor(&rv)whenretval == &rv. (not sure)Steps to Reproduce
cargo test -p tests -- --ignored prop_string_field_does_not_leak_on_repeated_get_messageExample
Extension Code:
PHP Code:
Actual Behavior
One
zend_stringorphaned pergetMessage()call. Linear memory growth.Expected Behavior
Stable memory across repeated reads.
PHP Version
ext-php-rs Version
master + PR #737
Operating System
Linux