From a25bce09ae2dc3596b4ac5d5940c715cb790b08d 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. The object's IDispatch typeinfo already names that library through GetContainingTypeLib(), including when autoregister_typelib is off. The sink path uses that library and keeps typeinfo NULL when GetTypeInfoOfGuid() fails. 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. Print-false and objects without IProvideClassInfo2 are unchanged. Fixes GH-25008 --- NEWS | 4 +++ ext/com_dotnet/com_typeinfo.c | 31 ++++++++++++++-- ext/com_dotnet/tests/gh25008.phpt | 59 +++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 2 deletions(-) create mode 100755 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..31885105ac0f 100644 --- a/ext/com_dotnet/com_typeinfo.c +++ b/ext/com_dotnet/com_typeinfo.c @@ -326,6 +326,27 @@ ITypeInfo *php_com_locate_typeinfo(zend_string *type_lib_name, php_com_dotnet_ob php_error_docref(NULL, E_WARNING, "IProvideClassInfo: this code not yet written!"); IProvideClassInfo_Release(pci); } + if (gotguid) { + ITypeInfo *srcinfo = obj->typeinfo; + bool release_srcinfo = false; + unsigned int idx; + ITypeLib *container = NULL; + + if (!srcinfo) { + ITypeInfo *fetched = NULL; + + if (SUCCEEDED(IDispatch_GetTypeInfo(V_DISPATCH(&obj->v), 0, LANG_NEUTRAL, &fetched)) && fetched) { + srcinfo = fetched; + release_srcinfo = true; + } + } + if (srcinfo && SUCCEEDED(ITypeInfo_GetContainingTypeLib(srcinfo, &container, &idx))) { + typelib = container; + } + if (release_srcinfo) { + ITypeInfo_Release(srcinfo); + } + } } } else if (dispatch_name == NULL) { if (obj->typeinfo) { @@ -408,8 +429,12 @@ ITypeInfo *php_com_locate_typeinfo(zend_string *type_lib_name, php_com_dotnet_ob efree(olename); - } else if (gotguid) { - ITypeLib_GetTypeInfoOfGuid(typelib, &iid, &typeinfo); + } else if (gotguid && typelib) { + ITypeInfo *found = NULL; + + if (SUCCEEDED(ITypeLib_GetTypeInfoOfGuid(typelib, &iid, &found))) { + typeinfo = found; + } } if (typelib) { @@ -639,6 +664,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 100755 index 000000000000..19e461d34519 --- /dev/null +++ b/ext/com_dotnet/tests/gh25008.phpt @@ -0,0 +1,59 @@ +--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)