Commit 2c4fdd71b3f for php
commit 2c4fdd71b3f94828402a7b90666357ce41090e3e
Author: Ilia Alshanetsky <ilia@ilia.ws>
Date: Wed Oct 28 14:26:57 2026 -0400
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
Closes GH-25020
diff --git a/NEWS b/NEWS
index 68b413c08ee..daf52ae14d9 100644
--- a/NEWS
+++ b/NEWS
@@ -52,6 +52,10 @@ PHP NEWS
(ndossche, devnexen)
. Fixed bug GH-9670 (List -C and -q in the CLI help). (Juan Versolato Lopes)
+- 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 28306b5609b..3917437ebbc 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 00000000000..eba601c9d1d
--- /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--
+<?php
+try {
+ new COM('WinHttp.WinHttpRequest.5.1');
+ new COM('Scripting.Dictionary');
+} catch (com_exception $e) {
+ die('skip WinHttp or Scripting.Dictionary is not available');
+}
+?>
+--FILE--
+<?php
+$o = new COM('WinHttp.WinHttpRequest.5.1', null, CP_UTF8);
+
+try {
+ var_dump(com_event_sink($o, new stdClass));
+ echo "sink: no error\n";
+} catch (Error $e) {
+ echo 'sink: ', $e::class, ': ', $e->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)