From a05ef27e47e076405f621300083bb21699b1fadb Mon Sep 17 00:00:00 2001 From: "Christoph M. Becker" Date: Wed, 9 Oct 2024 20:39:53 +0200 Subject: [PATCH] Fix property access of PHP objects wrapped in variant First, we fix the long standing issue that property access throws a `com_exception` ("0x80020003: member not found), because the `HRESULT` was not properly set after accessing the property. Next, we fix an issue introduced as of PHP 7.0.0, where the string length for write access had been properly adapted, but the string length for read access had been overlooked. Then we fix an issue introduced as of PHP 8.0.0, where new `HashTable`s no longer set `nNextFreeElement` to zero, but to `ZEND_LONG_MIN`. This doesn't work well with the `DISPID` lookup, which is a `LONG`. Finally we fix a potential double-free due to erroneously destroying the return value of `zend_read_property()`. --- ext/com_dotnet/com_wrapper.c | 16 +++++----- ext/com_dotnet/tests/variant_variation2.phpt | 33 ++++++++++++++++++++ 2 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 ext/com_dotnet/tests/variant_variation2.phpt diff --git a/ext/com_dotnet/com_wrapper.c b/ext/com_dotnet/com_wrapper.c index e505dc654026f..42698a2e651f5 100644 --- a/ext/com_dotnet/com_wrapper.c +++ b/ext/com_dotnet/com_wrapper.c @@ -258,9 +258,11 @@ static HRESULT STDMETHODCALLTYPE disp_invokeex( * and expose it as a COM exception */ if (wFlags & DISPATCH_PROPERTYGET) { - retval = zend_read_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name)+1, 1, &rv); + retval = zend_read_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), 1, &rv); + ret = S_OK; } else if (wFlags & DISPATCH_PROPERTYPUT) { zend_update_property(Z_OBJCE(disp->object), Z_OBJ(disp->object), Z_STRVAL_P(name), Z_STRLEN_P(name), ¶ms[0]); + ret = S_OK; } else if (wFlags & DISPATCH_METHOD) { zend_try { retval = &rv; @@ -305,7 +307,7 @@ static HRESULT STDMETHODCALLTYPE disp_invokeex( VariantInit(pvarRes); php_com_variant_from_zval(pvarRes, retval, COMG(code_page)); } - zval_ptr_dtor(retval); + // zval_ptr_dtor(retval); // TODO needed for function calls? } else if (pvarRes) { VariantInit(pvarRes); } @@ -425,7 +427,7 @@ static void generate_dispids(php_dispatchex *disp) zend_string *name = NULL; zval *tmp, tmp2; int keytype; - zend_ulong pid; + zend_long pid; if (disp->dispid_to_name == NULL) { ALLOC_HASHTABLE(disp->dispid_to_name); @@ -458,8 +460,8 @@ static void generate_dispids(php_dispatchex *disp) /* add the mappings */ ZVAL_STR_COPY(&tmp2, name); - pid = zend_hash_next_free_element(disp->dispid_to_name); - zend_hash_index_update(disp->dispid_to_name, pid, &tmp2); + zend_hash_next_index_insert(disp->dispid_to_name, &tmp2); + pid = zend_hash_next_free_element(disp->dispid_to_name) - 1; ZVAL_LONG(&tmp2, pid); zend_hash_update(disp->name_to_dispid, name, &tmp2); @@ -493,8 +495,8 @@ static void generate_dispids(php_dispatchex *disp) /* add the mappings */ ZVAL_STR_COPY(&tmp2, name); - pid = zend_hash_next_free_element(disp->dispid_to_name); - zend_hash_index_update(disp->dispid_to_name, pid, &tmp2); + zend_hash_next_index_insert(disp->dispid_to_name, &tmp2); + pid = zend_hash_next_free_element(disp->dispid_to_name) - 1; ZVAL_LONG(&tmp2, pid); zend_hash_update(disp->name_to_dispid, name, &tmp2); diff --git a/ext/com_dotnet/tests/variant_variation2.phpt b/ext/com_dotnet/tests/variant_variation2.phpt new file mode 100644 index 0000000000000..5a93967b26383 --- /dev/null +++ b/ext/com_dotnet/tests/variant_variation2.phpt @@ -0,0 +1,33 @@ +--TEST-- +Testing reading and writing of properties +--EXTENSIONS-- +com_dotnet +--FILE-- +foo); +var_dump($v->bar); +$v->foo = "new foo"; +var_dump($v->foo instanceof variant); +var_dump((string) $v->foo); +var_dump($o->foo instanceof variant); +var_dump((string) $o->foo); +$v->bar = "new bar"; +var_dump($v->bar); +var_dump($o->bar); +?> +--EXPECT-- +string(3) "foo" +string(3) "bar" +bool(true) +string(7) "new foo" +bool(true) +string(7) "new foo" +string(7) "new bar" +string(7) "new bar"