From 20c91398dac5c34838760dc0c590d5d28f2a2849 Mon Sep 17 00:00:00 2001 From: Ilia Alshanetsky Date: Wed, 28 Oct 2026 14:26:57 -0400 Subject: [PATCH] ext/com_dotnet: Fix GH-25008 event source typelib crash php_com_locate_typeinfo() stored the default source IID and called ITypeLib_GetTypeInfoOfGuid() on a NULL typelib. Resolve the source interface from the coclass instead: IProvideClassInfo2::GetClassInfo() returns it, and GetRefTypeInfo() on the implemented interface with that IID follows references into other type libraries. A source that is not a dispinterface still raises "Type kind must be dispatchable"; php_com_process_typeinfo() returned true after that error, so com_event_sink() walked an uninitialized name map. It now returns false. Fixes GH-25008 --- NEWS | 4 +++ ext/com_dotnet/com_typeinfo.c | 45 ++++++++++++++++++++++-- ext/com_dotnet/tests/gh25008.phpt | 58 +++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 ext/com_dotnet/tests/gh25008.phpt diff --git a/NEWS b/NEWS index 2b7a26cb8bb7..a79edf8c27ae 100644 --- a/NEWS +++ b/NEWS @@ -37,6 +37,10 @@ PHP NEWS . Fixed bug GH-24697 (PHP_HTTP_PARSER_STRICT build option doesn't work). (ndossche, devnexen) +- COM: + . Fixed bug GH-25008 (com_event_sink() and com_print_typeinfo() crash + resolving an event source with no loaded typelib). (Ilia Alshanetsky) + - Curl: . Fix use-after-free of CURLOPT_STDERR stream on cloned curl handle. (lacatoire) diff --git a/ext/com_dotnet/com_typeinfo.c b/ext/com_dotnet/com_typeinfo.c index 28306b5609b0..3917437ebbcf 100644 --- a/ext/com_dotnet/com_typeinfo.c +++ b/ext/com_dotnet/com_typeinfo.c @@ -302,6 +302,39 @@ PHP_COM_DOTNET_API ITypeLib *php_com_load_typelib_via_cache(const char *search_s return TL; } +static ITypeInfo *php_com_find_impl_typeinfo(ITypeInfo *coinfo, const GUID *iid) +{ + ITypeInfo *found = NULL; + TYPEATTR *coattr; + + if (FAILED(ITypeInfo_GetTypeAttr(coinfo, &coattr))) { + return NULL; + } + + for (unsigned int i = 0; i < coattr->cImplTypes && !found; i++) { + HREFTYPE rt; + ITypeInfo *implinfo; + TYPEATTR *implattr; + + if (FAILED(ITypeInfo_GetRefTypeOfImplType(coinfo, i, &rt)) || + FAILED(ITypeInfo_GetRefTypeInfo(coinfo, rt, &implinfo))) { + continue; + } + if (SUCCEEDED(ITypeInfo_GetTypeAttr(implinfo, &implattr))) { + if (IsEqualGUID(&implattr->guid, iid)) { + found = implinfo; + } + ITypeInfo_ReleaseTypeAttr(implinfo, implattr); + } + if (!found) { + ITypeInfo_Release(implinfo); + } + } + + ITypeInfo_ReleaseTypeAttr(coinfo, coattr); + return found; +} + ITypeInfo *php_com_locate_typeinfo(zend_string *type_lib_name, php_com_dotnet_object *obj, zend_string *dispatch_name, bool sink) { @@ -318,6 +351,14 @@ ITypeInfo *php_com_locate_typeinfo(zend_string *type_lib_name, php_com_dotnet_ob if (SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v), &IID_IProvideClassInfo2, (void**)&pci2))) { gotguid = SUCCEEDED(IProvideClassInfo2_GetGUID(pci2, GUIDKIND_DEFAULT_SOURCE_DISP_IID, &iid)); + if (gotguid) { + ITypeInfo *coinfo; + + if (SUCCEEDED(IProvideClassInfo2_GetClassInfo(pci2, &coinfo))) { + typeinfo = php_com_find_impl_typeinfo(coinfo, &iid); + ITypeInfo_Release(coinfo); + } + } IProvideClassInfo2_Release(pci2); } if (!gotguid && SUCCEEDED(IDispatch_QueryInterface(V_DISPATCH(&obj->v), &IID_IProvideClassInfo, (void**)&pci))) { @@ -408,8 +449,6 @@ ITypeInfo *php_com_locate_typeinfo(zend_string *type_lib_name, php_com_dotnet_ob efree(olename); - } else if (gotguid) { - ITypeLib_GetTypeInfoOfGuid(typelib, &iid, &typeinfo); } if (typelib) { @@ -639,6 +678,8 @@ bool php_com_process_typeinfo(ITypeInfo *typeinfo, HashTable *id_to_name, bool p } } else { zend_throw_error(NULL, "Type kind must be dispatchable, %08x given", attr->typekind); + ITypeInfo_ReleaseTypeAttr(typeinfo, attr); + return false; } ITypeInfo_ReleaseTypeAttr(typeinfo, attr); diff --git a/ext/com_dotnet/tests/gh25008.phpt b/ext/com_dotnet/tests/gh25008.phpt new file mode 100644 index 000000000000..eba601c9d1d0 --- /dev/null +++ b/ext/com_dotnet/tests/gh25008.phpt @@ -0,0 +1,58 @@ +--TEST-- +GH-25008 (com_event_sink and com_print_typeinfo crash on a NULL source typelib) +--EXTENSIONS-- +com_dotnet +--INI-- +com.autoregister_typelib=0 +--SKIPIF-- + +--FILE-- +getMessage(), "\n"; +} + +ob_start(); +$events = com_print_typeinfo($o, null, true); +$events_dump = ob_get_clean(); +echo 'events: '; +var_dump($events); +echo (str_contains($events_dump, 'class IWinHttpRequestEvents') + && str_contains($events_dump, '{F97F4E15-B787-4212-80D1-D380CBBF982E}') + && str_contains($events_dump, 'function OnResponseStart')) + ? "events interface\n" : "events missing\n"; + +ob_start(); +$request = com_print_typeinfo($o, null, false); +$request_dump = ob_get_clean(); +echo 'request: '; +var_dump($request); +echo (str_contains($request_dump, 'class IWinHttpRequest') + && str_contains($request_dump, '{016FE2EC-B2C8-45F8-B23B-39E53A75396B}')) + ? "request interface\n" : "request missing\n"; + +$d = new COM('Scripting.Dictionary'); +echo 'dictionary: '; +var_dump(com_event_sink($d, new stdClass)); +?> +--EXPECTF-- +sink: Error: Type kind must be dispatchable, 00000003 given +events: bool(true) +events interface +request: bool(true) +request interface +dictionary: +Warning: com_event_sink(): IProvideClassInfo: this code not yet written! in %s on line %d +bool(false)