Commit 59f39b32 authored by Kevin Puetz's avatar Kevin Puetz Committed by Alexandre Julliard

rpcrt4/tests: Add tests for lifetime of IRpcChannelBuffer.

It's possible for a proxy to be released during the middle of an Invoke. E.g. a specific case where this happened was a single-shot event sink which, upon receiving the event it was waiting for, would immediately call DispEventUnadvise. This removed the proxy pointing to that sink from the connection point's list of subscribers and released the last refcount on the proxy itself. Therefore, all state used to complete an RPC call must be on the stack; once NdrProxySendReceive pumps STA messages and permits reentrancy, the proxy cannot be accessed or relied on to own anything. Add test showing MIDL_STUB_MESSAGE::pRpcChannelBuffer owns a refcount (to ensure it can read [out] parameters from the channel)
parent 92d74369
......@@ -1415,6 +1415,140 @@ static void test_delegated_methods(void)
ok(hr == S_OK, "got %#lx\n", hr);
}
typedef struct tagChannelBufferRefCount
{
IRpcChannelBuffer IRpcChannelBuffer_iface;
LONG RefCount;
} CChannelBufferRefCount;
static CChannelBufferRefCount* impl_from_IRpcChannelBuffer(IRpcChannelBuffer* iface)
{
return CONTAINING_RECORD(iface, CChannelBufferRefCount, IRpcChannelBuffer_iface);
}
static HRESULT WINAPI test_chanbuf_refcount_chan_query_interface(IRpcChannelBuffer* pchan,
REFIID iid,
void** ppv)
{
if (IsEqualGUID(&IID_IRpcChannelBuffer, iid))
{
*ppv = pchan;
IRpcChannelBuffer_AddRef(pchan);
return S_OK;
}
return E_NOINTERFACE;
}
static ULONG WINAPI test_chanbuf_refcount_chan_add_ref(IRpcChannelBuffer* pchan)
{
CChannelBufferRefCount* This = impl_from_IRpcChannelBuffer(pchan);
return InterlockedIncrement(&This->RefCount);
}
static ULONG WINAPI test_chanbuf_refcount_chan_release(IRpcChannelBuffer* pchan)
{
CChannelBufferRefCount* This = impl_from_IRpcChannelBuffer(pchan);
return InterlockedDecrement(&This->RefCount);
}
static HRESULT WINAPI test_chanbuf_refcount_chan_get_buffer(IRpcChannelBuffer* pchan,
RPCOLEMESSAGE* msg,
REFIID iid)
{
ok(0, "call to GetBuffer not expected\n");
return E_NOTIMPL;
}
static HRESULT WINAPI test_chanbuf_refcount_chan_send_receive(IRpcChannelBuffer* pchan,
RPCOLEMESSAGE* pMessage,
ULONG* pStatus)
{
ok(0, "call to SendReceive not expected\n");
return E_NOTIMPL;
}
static HRESULT WINAPI test_chanbuf_refcount_chan_free_buffer(IRpcChannelBuffer* pchan,
RPCOLEMESSAGE* pMessage)
{
ok(0, "call to FreeBuffer not expected\n");
return E_NOTIMPL;
}
static HRESULT WINAPI test_chanbuf_refcount_chan_get_dest_ctx(IRpcChannelBuffer* pchan,
DWORD* pdwDestContext,
void** ppvDestContext)
{
*pdwDestContext = MSHCTX_LOCAL;
*ppvDestContext = NULL;
return S_OK;
}
static HRESULT WINAPI test_chanbuf_refcount_chan_is_connected(IRpcChannelBuffer* pchan)
{
ok(0, "call to IsConnected not expected\n");
return E_NOTIMPL;
}
static IRpcChannelBufferVtbl test_chanbuf_refcount_test_rpc_chan_vtbl =
{
test_chanbuf_refcount_chan_query_interface,
test_chanbuf_refcount_chan_add_ref,
test_chanbuf_refcount_chan_release,
test_chanbuf_refcount_chan_get_buffer,
test_chanbuf_refcount_chan_send_receive,
test_chanbuf_refcount_chan_free_buffer,
test_chanbuf_refcount_chan_get_dest_ctx,
test_chanbuf_refcount_chan_is_connected
};
static void test_ChannelBufferRefCount(IPSFactoryBuffer *ppsf)
{
IRpcProxyBuffer* proxy_buffer = NULL;
IUnknown* proxy_if1 = NULL;
CChannelBufferRefCount test_chanbuf = {{&test_chanbuf_refcount_test_rpc_chan_vtbl}, 1};
RPC_MESSAGE rpcMessage = {0};
MIDL_STUB_MESSAGE stubMessage = {0};
MIDL_STUB_DESC stubDesc = {0};
ULONG refs;
HRESULT hr = IPSFactoryBuffer_CreateProxy(ppsf, NULL, &IID_if1, &proxy_buffer, (void**)&proxy_if1);
ok(hr == S_OK, "got %#lx\n", hr);
ok(test_chanbuf.RefCount == 1, "got %ld\n", test_chanbuf.RefCount);
hr = IRpcProxyBuffer_Connect(proxy_buffer, &test_chanbuf.IRpcChannelBuffer_iface);
ok(hr == S_OK, "got %#lx\n", hr);
/* proxy_buffer should have acquired its own refcount on test_chanbuf */
ok(test_chanbuf.RefCount == 2, "got %ld\n", test_chanbuf.RefCount);
/* which therefore survives releasing the initial one */
refs = IRpcChannelBuffer_Release(&test_chanbuf.IRpcChannelBuffer_iface);
ok(refs == 1, "got %ld\n", refs);
NdrProxyInitialize(proxy_if1, &rpcMessage, &stubMessage, &stubDesc, 0);
/* stubMessage should add its own refcount on test_chanbuf */
todo_wine ok(test_chanbuf.RefCount == 2, "got %ld\n", test_chanbuf.RefCount);
ok(stubMessage.pRpcChannelBuffer != NULL, "NULL pRocChannelBuffer\n");
/* stubMessage doesn't add its own refcounts on proxy_if1 or proxy_buffer,
* so it's possible these are freed out from under it.
* E.g. an event sink might unadvise upon receiving the event it was waiting for;
* this unadvise could be reentrant to Invoke because SendReceive pumps STA messages.
* The source would then erase that conection point entry and Release the proxy. */
IRpcProxyBuffer_Disconnect(proxy_buffer);
todo_wine ok(test_chanbuf.RefCount == 1, "got %ld\n", test_chanbuf.RefCount);
IRpcProxyBuffer_Release(proxy_buffer);
refs = IUnknown_Release(proxy_if1);
ok(refs == 0, "got %ld\n", refs);
todo_wine ok(test_chanbuf.RefCount == 1, "got %ld\n", test_chanbuf.RefCount);
/* NdrProxyFreeBuffer must not dereference the the now-freed proxy_if1,
* yet should still free the remaining reference on test_chanbuf */
NdrProxyFreeBuffer(proxy_if1, &stubMessage);
ok(test_chanbuf.RefCount == 0, "got %ld\n", test_chanbuf.RefCount);
todo_wine ok(!stubMessage.pRpcChannelBuffer, "dangling pRpcChannelBuffer = %p\n", stubMessage.pRpcChannelBuffer);
}
START_TEST( cstub )
{
IPSFactoryBuffer *ppsf;
......@@ -1440,6 +1574,7 @@ START_TEST( cstub )
test_delegating_Invoke(ppsf);
test_NdrDllRegisterProxy();
test_delegated_methods();
test_ChannelBufferRefCount(ppsf);
OleUninitialize();
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment