When an application has both apartment-threaded and multi-threaded apartments then apartment_findfromtid() might return just the first one, and later stub manager assiciated with that apartment may not have an appropriate connection. In order to find the appropriate connection it's needed to check all apartment types belonging to a thread.
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru --- dlls/combase/apartment.c | 4 ++-- dlls/combase/combase_private.h | 2 +- dlls/combase/stubmanager.c | 34 +++++++++++++++++++++++++++++----- 3 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/dlls/combase/apartment.c b/dlls/combase/apartment.c index 108d6a71c5c..23dfaa5da2c 100644 --- a/dlls/combase/apartment.c +++ b/dlls/combase/apartment.c @@ -656,14 +656,14 @@ struct apartment * apartment_findfromoxid(OXID oxid) /* gets the apartment which has a given creator thread ID. The caller must * release the reference from the apartment as soon as the apartment pointer * is no longer required. */ -struct apartment * apartment_findfromtid(DWORD tid) +struct apartment * apartment_findfromtid(DWORD tid, BOOL multi_threaded) { struct apartment *result = NULL, *apt;
EnterCriticalSection(&apt_cs); LIST_FOR_EACH_ENTRY(apt, &apts, struct apartment, entry) { - if (apt->tid == tid) + if (apt->tid == tid && apt->multi_threaded == multi_threaded) { result = apt; apartment_addref(result); diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index 9247af4ebb6..7d67e6f439a 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -170,7 +170,7 @@ IUnknown *com_get_registered_class_object(const struct apartment *apartment, REF DWORD clscontext) DECLSPEC_HIDDEN; void apartment_revoke_all_classes(const struct apartment *apt) DECLSPEC_HIDDEN; struct apartment * apartment_findfromoxid(OXID oxid) DECLSPEC_HIDDEN; -struct apartment * apartment_findfromtid(DWORD tid) DECLSPEC_HIDDEN; +struct apartment * apartment_findfromtid(DWORD tid, BOOL multi_threaded) DECLSPEC_HIDDEN;
HRESULT marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags) DECLSPEC_HIDDEN; diff --git a/dlls/combase/stubmanager.c b/dlls/combase/stubmanager.c index e9f20962e3b..8a910f63190 100644 --- a/dlls/combase/stubmanager.c +++ b/dlls/combase/stubmanager.c @@ -499,21 +499,45 @@ static HRESULT ipid_to_ifstub(const IPID *ipid, struct apartment **stub_apt, /* FIXME: hack for IRemUnknown */ if (ipid->Data2 == 0xffff) *stub_apt = apartment_findfromoxid(*(const OXID *)ipid->Data4); - else - *stub_apt = apartment_findfromtid(ipid->Data2); + else /* Look for multi-threaded apartment */ + *stub_apt = apartment_findfromtid(ipid->Data2, TRUE); + + TRACE("ipid %s (tid %08x) => stub_apt %p\n", debugstr_guid(ipid), ipid->Data2, *stub_apt); + if (!*stub_apt) { TRACE("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2); return RPC_E_INVALID_OBJECT; } *stubmgr_ret = get_stub_manager_from_ipid(*stub_apt, ipid, ifstub); - if (!*stubmgr_ret) + TRACE("stub_apt %p, ipid %s (tid %08x) => stubmgr_ret %p\n", *stub_apt, debugstr_guid(ipid), ipid->Data2, *stubmgr_ret); + + if (*stubmgr_ret) return S_OK; + + apartment_release(*stub_apt); + + if (ipid->Data2 == 0xffff) /* IRemUnknown */ { - apartment_release(*stub_apt); *stub_apt = NULL; return RPC_E_INVALID_OBJECT; } - return S_OK; + + /* Look for apartment-threaded apartment */ + *stub_apt = apartment_findfromtid(ipid->Data2, FALSE); + if (!*stub_apt) + { + TRACE("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2); + return RPC_E_INVALID_OBJECT; + } + + *stubmgr_ret = get_stub_manager_from_ipid(*stub_apt, ipid, ifstub); + TRACE("stub_apt %p, ipid %s (tid %08x) => stubmgr_ret %p\n", *stub_apt, debugstr_guid(ipid), ipid->Data2, *stubmgr_ret); + + if (*stubmgr_ret) return S_OK; + + apartment_release(*stub_apt); + *stub_apt = NULL; + return RPC_E_INVALID_OBJECT; }
static HRESULT ipid_to_stub_manager(const IPID *ipid, struct apartment **stub_apt, struct stub_manager **stub)
On Mon, Jul 19, 2021 at 06:27:54PM +0300, Dmitry Timoshkov wrote:
When an application has both apartment-threaded and multi-threaded apartments then apartment_findfromtid() might return just the first one, and later stub manager assiciated with that apartment may not have an appropriate connection. In order to find the appropriate connection it's needed to check all apartment types belonging to a thread.
Signed-off-by: Dmitry Timoshkov dmitry@baikal.ru
dlls/combase/apartment.c | 4 ++-- dlls/combase/combase_private.h | 2 +- dlls/combase/stubmanager.c | 34 +++++++++++++++++++++++++++++----- 3 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/dlls/combase/apartment.c b/dlls/combase/apartment.c index 108d6a71c5c..23dfaa5da2c 100644 --- a/dlls/combase/apartment.c +++ b/dlls/combase/apartment.c @@ -656,14 +656,14 @@ struct apartment * apartment_findfromoxid(OXID oxid) /* gets the apartment which has a given creator thread ID. The caller must
- release the reference from the apartment as soon as the apartment pointer
- is no longer required. */
-struct apartment * apartment_findfromtid(DWORD tid) +struct apartment * apartment_findfromtid(DWORD tid, BOOL multi_threaded) { struct apartment *result = NULL, *apt;
EnterCriticalSection(&apt_cs); LIST_FOR_EACH_ENTRY(apt, &apts, struct apartment, entry) {
if (apt->tid == tid)
if (apt->tid == tid && apt->multi_threaded == multi_threaded) { result = apt; apartment_addref(result);
diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index 9247af4ebb6..7d67e6f439a 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -170,7 +170,7 @@ IUnknown *com_get_registered_class_object(const struct apartment *apartment, REF DWORD clscontext) DECLSPEC_HIDDEN; void apartment_revoke_all_classes(const struct apartment *apt) DECLSPEC_HIDDEN; struct apartment * apartment_findfromoxid(OXID oxid) DECLSPEC_HIDDEN; -struct apartment * apartment_findfromtid(DWORD tid) DECLSPEC_HIDDEN; +struct apartment * apartment_findfromtid(DWORD tid, BOOL multi_threaded) DECLSPEC_HIDDEN;
HRESULT marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags) DECLSPEC_HIDDEN; diff --git a/dlls/combase/stubmanager.c b/dlls/combase/stubmanager.c index e9f20962e3b..8a910f63190 100644 --- a/dlls/combase/stubmanager.c +++ b/dlls/combase/stubmanager.c @@ -499,21 +499,45 @@ static HRESULT ipid_to_ifstub(const IPID *ipid, struct apartment **stub_apt, /* FIXME: hack for IRemUnknown */ if (ipid->Data2 == 0xffff) *stub_apt = apartment_findfromoxid(*(const OXID *)ipid->Data4);
- else
*stub_apt = apartment_findfromtid(ipid->Data2);
- else /* Look for multi-threaded apartment */
*stub_apt = apartment_findfromtid(ipid->Data2, TRUE);
- TRACE("ipid %s (tid %08x) => stub_apt %p\n", debugstr_guid(ipid), ipid->Data2, *stub_apt);
- if (!*stub_apt) { TRACE("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2); return RPC_E_INVALID_OBJECT; }
Hi Dmitry,
This doesn't look right. At the very least, this won't work if the MTA isn't found, as it will return in the if block above. More fundamentally though, what's actually going on here? If the thread has joined an apartment-threaded apartment, then I'd expect that should be the one that's used. Some tests would help here.
Huw.
Hi Huw,
thanks for the review.
Huw Davies huw@codeweavers.com wrote:
This doesn't look right. At the very least, this won't work if the MTA isn't found, as it will return in the if block above. More fundamentally though, what's actually going on here? If the thread has joined an apartment-threaded apartment, then I'd expect that should be the one that's used. Some tests would help here.
This happens during the client application connecting to a server object residing in a separate process, that's a bit diffucult to replicate in a test. Manifestation of the problem on the client side is 0020:0024:err:ole:ifproxy_get_public_ref IRemUnknown_RemAddRef returned with 0x00000001, hrref = 0x80010114 This is a subsequence that on the server side we see 0198:019c:err:ole:get_stub_manager_from_ipid not found for ipid {00000001-019c-0198-b129-50e64a2d6135}
Here is a relevant part of the log (with some additional info added):
server: 0188:018c:trace:ole:stub_manager_new_ifstub m 041E9C38: ifstub 041E9D98 created with ipid {00000001-018c-0188-8366-ab230df0e7d9} client: 0020:0024:trace:ole:proxy_manager_create_ifproxy ifproxy 293FF3F8 created for IPID {00000001-018c-0188-8366-ab230df0e7d9}, interface {6d5140c1-7436-11ce-8034-00aa006009fa} with 0 public refs
and later when the client calls the server, the server side couldn't find a proper apartment: 0188:0198:trace:ole:dispatch_rpc ipid = {ffffffff-ffff-ffff-8c01-000088010000}, iMethod = 4 0188:0198:trace:ole:apartment_addref 1880000018c: before = 1 0188:0198:trace:ole:apartment_findfromoxid 1880000018c => 002B8BF8 (apartment-threaded) 0188:0198:trace:ole:ipid_to_ifstub ipid {ffffffff-ffff-ffff-8c01-000088010000} (tid 0000ffff) => stub_apt 002B8BF8 0188:0198:trace:ole:get_stub_manager_from_ipid 002B8BF8,{ffffffff-ffff-ffff-8c01-000088010000} 0188:0198:trace:ole:stub_manager_ipid_to_ifstub m 041E9C38: looking for ipid {ffffffff-ffff-ffff-8c01-000088010000} 0188:0198:trace:ole:stub_manager_ipid_to_ifstub m 041E9C38: ifstub 041E9D98, ifstub->ipid {00000001-018c-0188-8366-ab230df0e7d9} 0188:0198:trace:ole:stub_manager_ipid_to_ifstub m 041E9C38: ipid: {ffffffff-ffff-ffff-8c01-000088010000} => 00000000 0188:0198:trace:ole:stub_manager_ipid_to_ifstub m 041E8AA8: looking for ipid {ffffffff-ffff-ffff-8c01-000088010000} 0188:0198:trace:ole:stub_manager_ipid_to_ifstub m 041E8AA8: ifstub 041E8B48, ifstub->ipid {ffffffff-ffff-ffff-8c01-000088010000} 0188:0198:trace:ole:stub_manager_ipid_to_ifstub m 041E8AA8: ipid: {ffffffff-ffff-ffff-8c01-000088010000} => 041E8B48 0188:0198:trace:ole:stub_manager_int_addref before 1 0188:0198:trace:ole:get_stub_manager_from_ipid found 041E8AA8 for ipid {ffffffff-ffff-ffff-8c01-000088010000} 0188:0198:trace:ole:ipid_to_ifstub stub_apt 002B8BF8, ipid {ffffffff-ffff-ffff-8c01-000088010000} (tid 0000ffff) => stubmgr_ret 041E8AA8 0188:0198:trace:ole:CStdStubBuffer_AddRef (041E8B28)->AddRef() 0188:0198:trace:ole:dispatch_rpc Calling apartment thread 0x0000018c... ... 0188:018c:trace:ole:apartment_findfromtid 0000018c, multi-threaded 1 0188:018c:trace:ole:apartment_findfromtid apt 0034AD80, apt->tid 0000018c 0188:018c:trace:ole:apartment_addref 1880000cafe: before = 2 0188:018c:trace:ole:apartment_addref 1880000cafe: before = 3 0188:018c:trace:ole:apartment_findfromtid 018c => 0034AD80 (multi-threaded) 0188:018c:trace:ole:ipid_to_ifstub ipid {00000001-018c-0188-8366-ab230df0e7d9} (tid 0000018c) => stub_apt 0034AD80 0188:018c:trace:ole:get_stub_manager_from_ipid 0034AD80,{00000001-018c-0188-8366-ab230df0e7d9} 0188:018c:err:ole:get_stub_manager_from_ipid not found for ipid {00000001-018c-0188-8366-ab230df0e7d9}
Server side has both types of apartments in the thread 018c, and apartment_findfromtid() returns first one which happens to be a multi-threaded one, while stub manager to serve the client call resides in an apartment-threaded one.
Please let me know if more details is needed.
Hi Huw,
Huw Davies huw@codeweavers.com wrote:
This doesn't look right. At the very least, this won't work if the MTA isn't found, as it will return in the if block above. More fundamentally though, what's actually going on here? If the thread has joined an apartment-threaded apartment, then I'd expect that should be the one that's used. Some tests would help here.
I'm attaching the test that reproduces the problem. Since the app that I have here is .Net based it uses CoIncrementMTAUsage() that leads to an MTA creation, however creating an MTA in a separate thread (using CoInitializeEx(NULL, COINIT_MULTITHREADED) and leaving that thread run in background) reproduce this bug as well. The test runs fine under Windows however it crashes under Wine:
00b8:err:ole:get_stub_manager_from_ipid not found for ipid {00000001-00b8-00b4-8676-a9129dc206e7} 00b8:err:ole:unmarshal_ORPCTHIS invalid buffer length wine: Unhandled page fault on read access to 0302000D at address 019C903E (thread 00b8), starting debugger...
I'd appreciate any pointers or help with this bug.
Thanks.
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=94640
Your paranoid android.
=== w7u_2qxl (32 bit report) ===
ole32: ole_server.c:603: Test failed: server failed to terminate
=== w7u_el (32 bit report) ===
ole32: ole_server.c:603: Test failed: server failed to terminate
=== w8 (32 bit report) ===
ole32: ole_server.c:603: Test failed: server failed to terminate
=== w864 (32 bit report) ===
ole32: ole_server.c:603: Test failed: server failed to terminate
=== w1064v1507 (32 bit report) ===
ole32: ole_server: Timeout
=== w1064v1809 (32 bit report) ===
ole32: ole_server: Timeout
=== w1064 (32 bit report) ===
ole32: ole_server: Timeout
=== w1064_tsign (32 bit report) ===
ole32: ole_server: Timeout
=== w10pro64 (32 bit report) ===
ole32: ole_server: Timeout
=== w864 (64 bit report) ===
ole32: ole_server.c:603: Test failed: server failed to terminate
=== w1064v1507 (64 bit report) ===
ole32: ole_server: Timeout
=== w1064v1809 (64 bit report) ===
ole32: ole_server: Timeout
=== w1064 (64 bit report) ===
ole32: ole_server: Timeout
=== w1064_2qxl (64 bit report) ===
ole32: ole_server: Timeout
=== w1064_tsign (64 bit report) ===
ole32: ole_server: Timeout
=== w10pro64 (64 bit report) ===
ole32: ole_server: Timeout
=== w10pro64_ar (64 bit report) ===
ole32: ole_server: Timeout
=== w10pro64_he (64 bit report) ===
ole32: ole_server: Timeout
=== w10pro64_ja (64 bit report) ===
ole32: ole_server: Timeout
=== w10pro64_zh_CN (64 bit report) ===
ole32: ole_server: Timeout
=== debiant2 (32 bit report) ===
ole32: Unhandled exception: page fault on execute access to 0x0031fcb8 in 32-bit code (0x0031fcb8). Unhandled exception: page fault on read access to 0x0302000d in 32-bit code (0x685203f1).
=== debiant2 (32 bit Arabic:Morocco report) ===
ole32: Unhandled exception: page fault on execute access to 0x0031fcb8 in 32-bit code (0x0031fcb8). Unhandled exception: page fault on read access to 0x0302000d in 32-bit code (0x685203f1).
=== debiant2 (32 bit German report) ===
ole32: Unhandled exception: page fault on execute access to 0x0031fcb8 in 32-bit code (0x0031fcb8). Unhandled exception: page fault on read access to 0x0302000d in 32-bit code (0x685203f1).
=== debiant2 (32 bit French report) ===
ole32: Unhandled exception: page fault on execute access to 0x0031fcb8 in 32-bit code (0x0031fcb8). Unhandled exception: page fault on read access to 0x0302000d in 32-bit code (0x685203f1).
=== debiant2 (32 bit Hebrew:Israel report) ===
ole32: Unhandled exception: page fault on execute access to 0x0031fcb8 in 32-bit code (0x0031fcb8). Unhandled exception: page fault on read access to 0x0302000d in 32-bit code (0x685203f1).
=== debiant2 (32 bit Hindi:India report) ===
ole32: Unhandled exception: page fault on execute access to 0x0031fcb8 in 32-bit code (0x0031fcb8). Unhandled exception: page fault on read access to 0x0302000d in 32-bit code (0x685203f1).
=== debiant2 (32 bit Japanese:Japan report) ===
ole32: Unhandled exception: page fault on execute access to 0x0031fcb8 in 32-bit code (0x0031fcb8). Unhandled exception: page fault on read access to 0x0302000d in 32-bit code (0x685203f1).
=== debiant2 (32 bit Chinese:China report) ===
ole32: Unhandled exception: page fault on execute access to 0x0031fcb8 in 32-bit code (0x0031fcb8). Unhandled exception: page fault on read access to 0x0302000d in 32-bit code (0x685203f1).
=== debiant2 (32 bit WoW report) ===
ole32: ole_server.c:474: Test failed: CoCreateInstance(IID_IUnknown) error 0x80010111 Unhandled exception: page fault on read access to 0x0303000d in 32-bit code (0x685203f1).
=== debiant2 (64 bit WoW report) ===
ole32: Unhandled exception: page fault on read access to 0x0000006c in 64-bit code (0x000000006831d1c6). Unhandled exception: page fault on read access to 0x1003020015 in 64-bit code (0x000000006831da36).