Added patch to implement proper refcounting and locking for HGLOBAL based IStream.

This commit is contained in:
Sebastian Lackner 2016-02-06 04:27:32 +01:00
parent 8e6776a691
commit 74ac55e7d9
9 changed files with 1154 additions and 0 deletions

View File

@ -0,0 +1,279 @@
From 267315dd040ccfa527f8ba48038a268236ca1d29 Mon Sep 17 00:00:00 2001
From: Dmitry Timoshkov <dmitry@baikal.ru>
Date: Tue, 2 Feb 2016 12:49:16 +0800
Subject: ole32/tests: Add a bunch of tests for HGLOBAL based IStream::Clone.
---
dlls/ole32/tests/hglobalstream.c | 244 +++++++++++++++++++++++++++++++++++++++
1 file changed, 244 insertions(+)
diff --git a/dlls/ole32/tests/hglobalstream.c b/dlls/ole32/tests/hglobalstream.c
index e20d81b..0dc3c52 100644
--- a/dlls/ole32/tests/hglobalstream.c
+++ b/dlls/ole32/tests/hglobalstream.c
@@ -2,6 +2,7 @@
* Stream on HGLOBAL Tests
*
* Copyright 2006 Robert Shearman (for CodeWeavers)
+ * Copyright 2016 Dmitry Timoshkov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -508,11 +509,254 @@ static void test_freed_hglobal(void)
IStream_Release(pStream);
}
+static void stream_info(IStream *stream, HGLOBAL *hmem, int *size, int *pos)
+{
+ HRESULT hr;
+ STATSTG stat;
+ LARGE_INTEGER offset;
+ ULARGE_INTEGER newpos;
+
+ *hmem = 0;
+ *size = *pos = -1;
+
+ hr = GetHGlobalFromStream(stream, hmem);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ memset(&stat, 0x55, sizeof(stat));
+ hr = IStream_Stat(stream, &stat, STATFLAG_DEFAULT);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+ ok(stat.type == STGTY_STREAM, "unexpected %#x\n", stat.type);
+ ok(!stat.pwcsName, "unexpected %p\n", stat.pwcsName);
+ ok(IsEqualIID(&stat.clsid, &GUID_NULL), "unexpected %s\n", wine_dbgstr_guid(&stat.clsid));
+ ok(!stat.cbSize.HighPart, "unexpected %#x\n", stat.cbSize.HighPart);
+ *size = stat.cbSize.LowPart;
+
+ offset.QuadPart = 0;
+ hr = IStream_Seek(stream, offset, STREAM_SEEK_CUR, &newpos);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+ ok(!newpos.HighPart, "unexpected %#x\n", newpos.HighPart);
+ *pos = newpos.LowPart;
+}
+
+static void test_IStream_Clone(void)
+{
+ static const char hello[] = "Hello World!";
+ char buf[32];
+ HRESULT hr;
+ IStream *stream, *clone;
+ HGLOBAL orig_hmem, hmem, hmem_clone;
+ ULARGE_INTEGER newsize;
+ LARGE_INTEGER offset;
+ int size, pos, ret;
+
+ /* test simple case for Clone */
+ orig_hmem = GlobalAlloc(GMEM_MOVEABLE, 0);
+ ok(orig_hmem != 0, "unexpected %p\n", orig_hmem);
+ hr = CreateStreamOnHGlobal(orig_hmem, TRUE, &stream);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ stream_info(stream, &hmem, &size, &pos);
+ ok(hmem == orig_hmem, "handles should match\n");
+ ok(size == 0, "unexpected %d\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ hr = IStream_Clone(stream, &clone);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ hr = IStream_Write(stream, hello, sizeof(hello), NULL);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ stream_info(stream, &hmem, &size, &pos);
+ ok(hmem != 0, "unexpected %p\n", hmem);
+ ok(size == 13, "unexpected %d\n", size);
+ ok(pos == 13, "unexpected %d\n", pos);
+
+ stream_info(clone, &hmem_clone, &size, &pos);
+ ok(hmem_clone == hmem, "handles should match\n");
+todo_wine
+ ok(size == 13, "unexpected %d\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ buf[0] = 0;
+ hr = IStream_Read(clone, buf, sizeof(buf), NULL);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+todo_wine
+ ok(!strcmp(buf, hello), "wrong stream contents\n");
+
+ newsize.QuadPart = 0x8000;
+ hr = IStream_SetSize(stream, newsize);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ stream_info(stream, &hmem, &size, &pos);
+ ok(hmem != 0, "unexpected %p\n", hmem);
+ ok(hmem == orig_hmem, "unexpected %p\n", hmem);
+ ok(size == 0x8000, "unexpected %#x\n", size);
+ ok(pos == 13, "unexpected %d\n", pos);
+
+ stream_info(clone, &hmem_clone, &size, &pos);
+ ok(hmem_clone == hmem, "handles should match\n");
+todo_wine
+ ok(size == 0x8000, "unexpected %#x\n", size);
+todo_wine
+ ok(pos == 13, "unexpected %d\n", pos);
+
+ IStream_Release(clone);
+ IStream_Release(stream);
+
+ /* exploit GMEM_FIXED forced move for the same base streams */
+ orig_hmem = GlobalAlloc(GMEM_FIXED, 1);
+ ok(orig_hmem != 0, "unexpected %p\n", orig_hmem);
+ hr = CreateStreamOnHGlobal(orig_hmem, TRUE, &stream);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ hr = IStream_Clone(stream, &clone);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ stream_info(stream, &hmem, &size, &pos);
+ ok(hmem != 0, "unexpected %p\n", hmem);
+ ok(size == 1, "unexpected %d\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ stream_info(clone, &hmem_clone, &size, &pos);
+ ok(hmem_clone == hmem, "handles should match\n");
+ ok(size == 1, "unexpected %d\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ newsize.QuadPart = 0x8000;
+ hr = IStream_SetSize(stream, newsize);
+todo_wine
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ stream_info(stream, &hmem, &size, &pos);
+ ok(hmem != 0, "unexpected %p\n", hmem);
+todo_wine
+ ok(hmem != orig_hmem, "unexpected %p\n", hmem);
+todo_wine
+ ok(size == 0x8000, "unexpected %#x\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ stream_info(clone, &hmem_clone, &size, &pos);
+ ok(hmem_clone == hmem, "handles should match\n");
+todo_wine
+ ok(size == 0x8000, "unexpected %#x\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ IStream_Release(stream);
+ IStream_Release(clone);
+
+ /* exploit GMEM_FIXED forced move for different base streams */
+ orig_hmem = GlobalAlloc(GMEM_FIXED, 1);
+ ok(orig_hmem != 0, "unexpected %p\n", orig_hmem);
+ hr = CreateStreamOnHGlobal(orig_hmem, TRUE, &stream);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ hr = CreateStreamOnHGlobal(orig_hmem, TRUE, &clone);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ stream_info(stream, &hmem, &size, &pos);
+ ok(hmem != 0, "unexpected %p\n", hmem);
+ ok(size == 1, "unexpected %d\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ stream_info(clone, &hmem_clone, &size, &pos);
+ ok(hmem_clone == hmem, "handles should match\n");
+ ok(size == 1, "unexpected %d\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ newsize.QuadPart = 0x8000;
+ hr = IStream_SetSize(stream, newsize);
+todo_wine
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ stream_info(stream, &hmem, &size, &pos);
+ ok(hmem != 0, "unexpected %p\n", hmem);
+todo_wine
+ ok(hmem != orig_hmem, "unexpected %p\n", hmem);
+todo_wine
+ ok(size == 0x8000, "unexpected %#x\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ stream_info(clone, &hmem_clone, &size, &pos);
+todo_wine
+ ok(hmem_clone != hmem, "handles should not match\n");
+ ok(size == 1, "unexpected %#x\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ IStream_Release(stream);
+ /* releasing clone leads to test termination under windows
+ IStream_Release(clone);
+ */
+
+ /* test Release for a being cloned stream */
+ hr = CreateStreamOnHGlobal(0, TRUE, &stream);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ hr = IStream_Clone(stream, &clone);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ stream_info(stream, &hmem, &size, &pos);
+ ok(hmem != 0, "unexpected %p\n", hmem);
+ ok(size == 0, "unexpected %d\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ stream_info(clone, &hmem_clone, &size, &pos);
+ ok(hmem_clone == hmem, "handles should match\n");
+ ok(size == 0, "unexpected %#x\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ ret = IStream_Release(stream);
+ ok(ret == 0, "unexpected %d\n", ret);
+
+ newsize.QuadPart = 0x8000;
+ hr = IStream_SetSize(clone, newsize);
+todo_wine
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ stream_info(clone, &hmem_clone, &size, &pos);
+ ok(hmem_clone == hmem, "handles should match\n");
+todo_wine
+ ok(size == 0x8000, "unexpected %#x\n", size);
+ ok(pos == 0, "unexpected %d\n", pos);
+
+ hr = IStream_Write(clone, hello, sizeof(hello), NULL);
+todo_wine
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ stream_info(clone, &hmem_clone, &size, &pos);
+ ok(hmem_clone == hmem, "handles should match\n");
+todo_wine
+ ok(size == 0x8000, "unexpected %#x\n", size);
+todo_wine
+ ok(pos == 13, "unexpected %d\n", pos);
+
+ offset.QuadPart = 0;
+ hr = IStream_Seek(clone, offset, STREAM_SEEK_SET, NULL);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+
+ buf[0] = 0;
+ hr = IStream_Read(clone, buf, sizeof(buf), NULL);
+ ok(hr == S_OK, "unexpected %#x\n", hr);
+todo_wine
+ ok(!strcmp(buf, hello), "wrong stream contents\n");
+
+ stream_info(clone, &hmem_clone, &size, &pos);
+ ok(hmem_clone == hmem, "handles should match\n");
+todo_wine
+ ok(size == 0x8000, "unexpected %#x\n", size);
+todo_wine
+ ok(pos == 32, "unexpected %d\n", pos);
+
+ ret = IStream_Release(clone);
+ ok(ret == 0, "unexpected %d\n", ret);
+}
+
START_TEST(hglobalstream)
{
HRESULT hr;
IStream *pStream;
+ test_IStream_Clone();
+
hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream);
ok_ole_success(hr, "CreateStreamOnHGlobal");
--
2.7.0

View File

@ -0,0 +1,47 @@
From a26bfabac3940176f412896241ffab5c7f92baaa Mon Sep 17 00:00:00 2001
From: Dmitry Timoshkov <dmitry@baikal.ru>
Date: Tue, 2 Feb 2016 12:53:47 +0800
Subject: ole32: Add a check for hglobal pointer to GetHGlobalFromStream.
---
dlls/ole32/hglobalstream.c | 4 ++--
dlls/ole32/tests/hglobalstream.c | 6 ++++++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/dlls/ole32/hglobalstream.c b/dlls/ole32/hglobalstream.c
index 655e380..b86de56 100644
--- a/dlls/ole32/hglobalstream.c
+++ b/dlls/ole32/hglobalstream.c
@@ -621,10 +621,10 @@ HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
{
HGLOBALStreamImpl* pStream;
- if (pstm == NULL)
+ if (!pstm || !phglobal)
return E_INVALIDARG;
- pStream = (HGLOBALStreamImpl*) pstm;
+ pStream = impl_from_IStream(pstm);
/*
* Verify that the stream object was created with CreateStreamOnHGlobal.
diff --git a/dlls/ole32/tests/hglobalstream.c b/dlls/ole32/tests/hglobalstream.c
index 0dc3c52..0453e54 100644
--- a/dlls/ole32/tests/hglobalstream.c
+++ b/dlls/ole32/tests/hglobalstream.c
@@ -555,6 +555,12 @@ static void test_IStream_Clone(void)
hr = CreateStreamOnHGlobal(orig_hmem, TRUE, &stream);
ok(hr == S_OK, "unexpected %#x\n", hr);
+ hr = GetHGlobalFromStream(stream, NULL);
+ ok(hr == E_INVALIDARG, "unexpected %#x\n", hr);
+
+ hr = GetHGlobalFromStream(NULL, &hmem);
+ ok(hr == E_INVALIDARG, "unexpected %#x\n", hr);
+
stream_info(stream, &hmem, &size, &pos);
ok(hmem == orig_hmem, "handles should match\n");
ok(size == 0, "unexpected %d\n", size);
--
2.7.0

View File

@ -0,0 +1,422 @@
From 3c9954c3838d7504fad0213ade7363747e7a6805 Mon Sep 17 00:00:00 2001
From: Dmitry Timoshkov <dmitry@baikal.ru>
Date: Tue, 2 Feb 2016 13:03:58 +0800
Subject: ole32: Add a wrapper for memory block managed by HGLOBAL based
IStream.
Based on a suggestion of Sebastian Lackner <sebastian@fds-team.de>.
---
dlls/ole32/hglobalstream.c | 202 +++++++++++++++++++++++++++------------
dlls/ole32/tests/hglobalstream.c | 12 ---
2 files changed, 140 insertions(+), 74 deletions(-)
diff --git a/dlls/ole32/hglobalstream.c b/dlls/ole32/hglobalstream.c
index b86de56..5eb6320 100644
--- a/dlls/ole32/hglobalstream.c
+++ b/dlls/ole32/hglobalstream.c
@@ -5,6 +5,7 @@
* for streams contained supported by an HGLOBAL pointer.
*
* Copyright 1999 Francis Beaudet
+ * Copyright 2016 Dmitry Timoshkov
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -42,7 +43,96 @@
#include "wine/debug.h"
-WINE_DEFAULT_DEBUG_CHANNEL(storage);
+WINE_DEFAULT_DEBUG_CHANNEL(hglobalstream);
+
+struct handle_wrapper
+{
+ LONG ref;
+ HGLOBAL hglobal;
+ ULONG size;
+ BOOL delete_on_release;
+ CRITICAL_SECTION lock;
+};
+
+static void handle_addref(struct handle_wrapper *handle)
+{
+ InterlockedIncrement(&handle->ref);
+}
+
+static void handle_release(struct handle_wrapper *handle)
+{
+ ULONG ref = InterlockedDecrement(&handle->ref);
+
+ if (!ref)
+ {
+ if (handle->delete_on_release)
+ {
+ GlobalFree(handle->hglobal);
+ handle->hglobal = NULL;
+ }
+
+ DeleteCriticalSection(&handle->lock);
+ HeapFree(GetProcessHeap(), 0, handle);
+ }
+}
+
+static void *handle_lock(struct handle_wrapper *handle)
+{
+ return GlobalLock(handle->hglobal);
+}
+
+static void handle_unlock(struct handle_wrapper *handle)
+{
+ GlobalUnlock(handle->hglobal);
+}
+
+static HGLOBAL handle_gethglobal(struct handle_wrapper *handle)
+{
+ return handle->hglobal;
+}
+
+static HRESULT handle_setsize(struct handle_wrapper *handle, ULONG size)
+{
+ HRESULT hr = S_OK;
+
+ EnterCriticalSection(&handle->lock);
+
+ if (handle->size != size)
+ {
+ HGLOBAL hglobal = GlobalReAlloc(handle->hglobal, size, 0);
+ if (hglobal)
+ {
+ handle->hglobal = hglobal;
+ handle->size = size;
+ }
+ else
+ hr = E_OUTOFMEMORY;
+ }
+
+ LeaveCriticalSection(&handle->lock);
+ return hr;
+}
+
+static ULONG handle_getsize(struct handle_wrapper *handle)
+{
+ return handle->size;
+}
+
+static struct handle_wrapper *handle_create(HGLOBAL hglobal, BOOL delete_on_release)
+{
+ struct handle_wrapper *handle;
+
+ handle = HeapAlloc(GetProcessHeap(), 0, sizeof(*handle));
+ if (handle)
+ {
+ handle->ref = 1;
+ handle->hglobal = hglobal;
+ handle->size = GlobalSize(hglobal);
+ handle->delete_on_release = delete_on_release;
+ InitializeCriticalSection(&handle->lock);
+ }
+ return handle;
+}
/****************************************************************************
* HGLOBALStreamImpl definition.
@@ -55,14 +145,7 @@ typedef struct
IStream IStream_iface;
LONG ref;
- /* support for the stream */
- HGLOBAL supportHandle;
-
- /* if TRUE the HGLOBAL is destroyed when the stream is finally released */
- BOOL deleteOnRelease;
-
- /* size of the stream */
- ULARGE_INTEGER streamSize;
+ struct handle_wrapper *handle;
/* current position of the cursor */
ULARGE_INTEGER currentPosition;
@@ -114,12 +197,7 @@ static ULONG WINAPI HGLOBALStreamImpl_Release(
if (!ref)
{
- if (This->deleteOnRelease)
- {
- GlobalFree(This->supportHandle);
- This->supportHandle = NULL;
- }
-
+ handle_release(This->handle);
HeapFree(GetProcessHeap(), 0, This);
}
@@ -161,15 +239,15 @@ static HRESULT WINAPI HGLOBALStreamImpl_Read(
* Using the known size of the stream, calculate the number of bytes
* to read from the block chain
*/
- bytesToReadFromBuffer = min( This->streamSize.u.LowPart - This->currentPosition.u.LowPart, cb);
+ bytesToReadFromBuffer = min( handle_getsize(This->handle) - This->currentPosition.u.LowPart, cb);
/*
* Lock the buffer in position and copy the data.
*/
- supportBuffer = GlobalLock(This->supportHandle);
+ supportBuffer = handle_lock(This->handle);
if (!supportBuffer)
{
- WARN("read from invalid hglobal %p\n", This->supportHandle);
+ WARN("read from invalid hglobal %p\n", handle_gethglobal(This->handle));
*pcbRead = 0;
return S_OK;
}
@@ -189,7 +267,7 @@ static HRESULT WINAPI HGLOBALStreamImpl_Read(
/*
* Cleanup
*/
- GlobalUnlock(This->supportHandle);
+ handle_unlock(This->handle);
/*
* Always returns S_OK even if the end of the stream is reached before the
@@ -241,7 +319,7 @@ static HRESULT WINAPI HGLOBALStreamImpl_Write(
/*
* Verify if we need to grow the stream
*/
- if (newSize.u.LowPart > This->streamSize.u.LowPart)
+ if (newSize.u.LowPart > handle_getsize(This->handle))
{
/* grow stream */
HRESULT hr = IStream_SetSize(iface, newSize);
@@ -255,10 +333,10 @@ static HRESULT WINAPI HGLOBALStreamImpl_Write(
/*
* Lock the buffer in position and copy the data.
*/
- supportBuffer = GlobalLock(This->supportHandle);
+ supportBuffer = handle_lock(This->handle);
if (!supportBuffer)
{
- WARN("write to invalid hglobal %p\n", This->supportHandle);
+ WARN("write to invalid hglobal %p\n", handle_gethglobal(This->handle));
return S_OK;
}
@@ -272,7 +350,7 @@ static HRESULT WINAPI HGLOBALStreamImpl_Write(
/*
* Cleanup
*/
- GlobalUnlock(This->supportHandle);
+ handle_unlock(This->handle);
out:
/*
@@ -318,7 +396,7 @@ static HRESULT WINAPI HGLOBALStreamImpl_Seek(
case STREAM_SEEK_CUR:
break;
case STREAM_SEEK_END:
- newPosition = This->streamSize;
+ newPosition.QuadPart = handle_getsize(This->handle);
break;
default:
hr = STG_E_SEEKERROR;
@@ -363,29 +441,13 @@ static HRESULT WINAPI HGLOBALStreamImpl_SetSize(
ULARGE_INTEGER libNewSize) /* [in] */
{
HGLOBALStreamImpl* This = impl_from_IStream(iface);
- HGLOBAL supportHandle;
TRACE("(%p, %d)\n", iface, libNewSize.u.LowPart);
/*
* HighPart is ignored as shown in tests
*/
-
- if (This->streamSize.u.LowPart == libNewSize.u.LowPart)
- return S_OK;
-
- /*
- * Re allocate the HGlobal to fit the new size of the stream.
- */
- supportHandle = GlobalReAlloc(This->supportHandle, libNewSize.u.LowPart, 0);
-
- if (supportHandle == 0)
- return E_OUTOFMEMORY;
-
- This->supportHandle = supportHandle;
- This->streamSize.u.LowPart = libNewSize.u.LowPart;
-
- return S_OK;
+ return handle_setsize(This->handle, libNewSize.u.LowPart);
}
/***
@@ -533,24 +595,49 @@ static HRESULT WINAPI HGLOBALStreamImpl_Stat(
pstatstg->pwcsName = NULL;
pstatstg->type = STGTY_STREAM;
- pstatstg->cbSize = This->streamSize;
+ pstatstg->cbSize.QuadPart = handle_getsize(This->handle);
return S_OK;
}
+static const IStreamVtbl HGLOBALStreamImplVtbl;
+
+static HGLOBALStreamImpl *HGLOBALStreamImpl_Create(void)
+{
+ HGLOBALStreamImpl *This;
+
+ This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
+ if (This)
+ {
+ This->IStream_iface.lpVtbl = &HGLOBALStreamImplVtbl;
+ This->ref = 1;
+ }
+ return This;
+}
+
static HRESULT WINAPI HGLOBALStreamImpl_Clone(
IStream* iface,
IStream** ppstm) /* [out] */
{
HGLOBALStreamImpl* This = impl_from_IStream(iface);
+ HGLOBALStreamImpl* clone;
ULARGE_INTEGER dummy;
LARGE_INTEGER offset;
- HRESULT hr;
- TRACE(" Cloning %p (deleteOnRelease=%d seek position=%ld)\n",iface,This->deleteOnRelease,(long)This->currentPosition.QuadPart);
- hr = CreateStreamOnHGlobal(This->supportHandle, FALSE, ppstm);
- if(FAILED(hr))
- return hr;
+ if (!ppstm) return E_INVALIDARG;
+
+ *ppstm = NULL;
+
+ TRACE(" Cloning %p (seek position=%d)\n", iface, This->currentPosition.u.LowPart);
+
+ clone = HGLOBALStreamImpl_Create();
+ if (!clone) return E_OUTOFMEMORY;
+
+ *ppstm = &clone->IStream_iface;
+
+ handle_addref(This->handle);
+ clone->handle = This->handle;
+
offset.QuadPart = (LONGLONG)This->currentPosition.QuadPart;
IStream_Seek(*ppstm, offset, STREAM_SEEK_SET, &dummy);
return S_OK;
@@ -587,28 +674,19 @@ HRESULT WINAPI CreateStreamOnHGlobal(
if (!ppstm)
return E_INVALIDARG;
- This = HeapAlloc(GetProcessHeap(), 0, sizeof(HGLOBALStreamImpl));
+ This = HGLOBALStreamImpl_Create();
if (!This) return E_OUTOFMEMORY;
- This->IStream_iface.lpVtbl = &HGLOBALStreamImplVtbl;
- This->ref = 1;
-
- /* initialize the support */
- This->supportHandle = hGlobal;
- This->deleteOnRelease = fDeleteOnRelease;
-
/* allocate a handle if one is not supplied */
- if (!This->supportHandle)
- This->supportHandle = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_SHARE, 0);
+ if (!hGlobal)
+ hGlobal = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_SHARE, 0);
+
+ This->handle = handle_create(hGlobal, fDeleteOnRelease);
/* start at the beginning */
This->currentPosition.u.HighPart = 0;
This->currentPosition.u.LowPart = 0;
- /* initialize the size of the stream to the size of the handle */
- This->streamSize.u.HighPart = 0;
- This->streamSize.u.LowPart = GlobalSize(This->supportHandle);
-
*ppstm = &This->IStream_iface;
return S_OK;
@@ -630,7 +708,7 @@ HRESULT WINAPI GetHGlobalFromStream(IStream* pstm, HGLOBAL* phglobal)
* Verify that the stream object was created with CreateStreamOnHGlobal.
*/
if (pStream->IStream_iface.lpVtbl == &HGLOBALStreamImplVtbl)
- *phglobal = pStream->supportHandle;
+ *phglobal = handle_gethglobal(pStream->handle);
else
{
*phglobal = 0;
diff --git a/dlls/ole32/tests/hglobalstream.c b/dlls/ole32/tests/hglobalstream.c
index 0453e54..afda4e6 100644
--- a/dlls/ole32/tests/hglobalstream.c
+++ b/dlls/ole32/tests/hglobalstream.c
@@ -579,14 +579,12 @@ static void test_IStream_Clone(void)
stream_info(clone, &hmem_clone, &size, &pos);
ok(hmem_clone == hmem, "handles should match\n");
-todo_wine
ok(size == 13, "unexpected %d\n", size);
ok(pos == 0, "unexpected %d\n", pos);
buf[0] = 0;
hr = IStream_Read(clone, buf, sizeof(buf), NULL);
ok(hr == S_OK, "unexpected %#x\n", hr);
-todo_wine
ok(!strcmp(buf, hello), "wrong stream contents\n");
newsize.QuadPart = 0x8000;
@@ -601,9 +599,7 @@ todo_wine
stream_info(clone, &hmem_clone, &size, &pos);
ok(hmem_clone == hmem, "handles should match\n");
-todo_wine
ok(size == 0x8000, "unexpected %#x\n", size);
-todo_wine
ok(pos == 13, "unexpected %d\n", pos);
IStream_Release(clone);
@@ -715,24 +711,19 @@ todo_wine
newsize.QuadPart = 0x8000;
hr = IStream_SetSize(clone, newsize);
-todo_wine
ok(hr == S_OK, "unexpected %#x\n", hr);
stream_info(clone, &hmem_clone, &size, &pos);
ok(hmem_clone == hmem, "handles should match\n");
-todo_wine
ok(size == 0x8000, "unexpected %#x\n", size);
ok(pos == 0, "unexpected %d\n", pos);
hr = IStream_Write(clone, hello, sizeof(hello), NULL);
-todo_wine
ok(hr == S_OK, "unexpected %#x\n", hr);
stream_info(clone, &hmem_clone, &size, &pos);
ok(hmem_clone == hmem, "handles should match\n");
-todo_wine
ok(size == 0x8000, "unexpected %#x\n", size);
-todo_wine
ok(pos == 13, "unexpected %d\n", pos);
offset.QuadPart = 0;
@@ -742,14 +733,11 @@ todo_wine
buf[0] = 0;
hr = IStream_Read(clone, buf, sizeof(buf), NULL);
ok(hr == S_OK, "unexpected %#x\n", hr);
-todo_wine
ok(!strcmp(buf, hello), "wrong stream contents\n");
stream_info(clone, &hmem_clone, &size, &pos);
ok(hmem_clone == hmem, "handles should match\n");
-todo_wine
ok(size == 0x8000, "unexpected %#x\n", size);
-todo_wine
ok(pos == 32, "unexpected %d\n", pos);
ret = IStream_Release(clone);
--
2.7.0

View File

@ -0,0 +1,32 @@
From e6b772e0000dac0c50a8070b46413352e9c86174 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sat, 6 Feb 2016 03:26:10 +0100
Subject: ole32: Set DebugInfo->Spare[0] for handle_wrapper lock.
---
dlls/ole32/hglobalstream.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/dlls/ole32/hglobalstream.c b/dlls/ole32/hglobalstream.c
index 5eb6320..8b7e960 100644
--- a/dlls/ole32/hglobalstream.c
+++ b/dlls/ole32/hglobalstream.c
@@ -71,6 +71,7 @@ static void handle_release(struct handle_wrapper *handle)
handle->hglobal = NULL;
}
+ handle->lock.DebugInfo->Spare[0] = 0;
DeleteCriticalSection(&handle->lock);
HeapFree(GetProcessHeap(), 0, handle);
}
@@ -130,6 +131,7 @@ static struct handle_wrapper *handle_create(HGLOBAL hglobal, BOOL delete_on_rele
handle->size = GlobalSize(hglobal);
handle->delete_on_release = delete_on_release;
InitializeCriticalSection(&handle->lock);
+ handle->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": handle_wrapper.lock");
}
return handle;
}
--
2.7.0

View File

@ -0,0 +1,72 @@
From b7849b3a81d2e55ccfc3d61a8a14bbacf878438b Mon Sep 17 00:00:00 2001
From: Dmitry Timoshkov <dmitry@baikal.ru>
Date: Tue, 2 Feb 2016 15:35:59 +0800
Subject: ole32: Allow moving a being reallocated block of memory managed by
HGLOBAL based IStream.
---
dlls/ole32/hglobalstream.c | 2 +-
dlls/ole32/tests/hglobalstream.c | 8 --------
2 files changed, 1 insertion(+), 9 deletions(-)
diff --git a/dlls/ole32/hglobalstream.c b/dlls/ole32/hglobalstream.c
index 8b7e960..3e7f67b 100644
--- a/dlls/ole32/hglobalstream.c
+++ b/dlls/ole32/hglobalstream.c
@@ -100,7 +100,7 @@ static HRESULT handle_setsize(struct handle_wrapper *handle, ULONG size)
if (handle->size != size)
{
- HGLOBAL hglobal = GlobalReAlloc(handle->hglobal, size, 0);
+ HGLOBAL hglobal = GlobalReAlloc(handle->hglobal, size, GMEM_MOVEABLE);
if (hglobal)
{
handle->hglobal = hglobal;
diff --git a/dlls/ole32/tests/hglobalstream.c b/dlls/ole32/tests/hglobalstream.c
index afda4e6..9aa3b6c 100644
--- a/dlls/ole32/tests/hglobalstream.c
+++ b/dlls/ole32/tests/hglobalstream.c
@@ -626,20 +626,16 @@ static void test_IStream_Clone(void)
newsize.QuadPart = 0x8000;
hr = IStream_SetSize(stream, newsize);
-todo_wine
ok(hr == S_OK, "unexpected %#x\n", hr);
stream_info(stream, &hmem, &size, &pos);
ok(hmem != 0, "unexpected %p\n", hmem);
-todo_wine
ok(hmem != orig_hmem, "unexpected %p\n", hmem);
-todo_wine
ok(size == 0x8000, "unexpected %#x\n", size);
ok(pos == 0, "unexpected %d\n", pos);
stream_info(clone, &hmem_clone, &size, &pos);
ok(hmem_clone == hmem, "handles should match\n");
-todo_wine
ok(size == 0x8000, "unexpected %#x\n", size);
ok(pos == 0, "unexpected %d\n", pos);
@@ -667,19 +663,15 @@ todo_wine
newsize.QuadPart = 0x8000;
hr = IStream_SetSize(stream, newsize);
-todo_wine
ok(hr == S_OK, "unexpected %#x\n", hr);
stream_info(stream, &hmem, &size, &pos);
ok(hmem != 0, "unexpected %p\n", hmem);
-todo_wine
ok(hmem != orig_hmem, "unexpected %p\n", hmem);
-todo_wine
ok(size == 0x8000, "unexpected %#x\n", size);
ok(pos == 0, "unexpected %d\n", pos);
stream_info(clone, &hmem_clone, &size, &pos);
-todo_wine
ok(hmem_clone != hmem, "handles should not match\n");
ok(size == 1, "unexpected %#x\n", size);
ok(pos == 0, "unexpected %d\n", pos);
--
2.7.0

View File

@ -0,0 +1,115 @@
From f5c31437019892510e36f603cbde72d9204a4155 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sat, 6 Feb 2016 03:50:55 +0100
Subject: ole32: Improve thread-safety of HGLOBALStreamImpl_Read.
---
dlls/ole32/hglobalstream.c | 83 ++++++++++++++++++----------------------------
1 file changed, 32 insertions(+), 51 deletions(-)
diff --git a/dlls/ole32/hglobalstream.c b/dlls/ole32/hglobalstream.c
index 3e7f67b..27ac706 100644
--- a/dlls/ole32/hglobalstream.c
+++ b/dlls/ole32/hglobalstream.c
@@ -87,6 +87,34 @@ static void handle_unlock(struct handle_wrapper *handle)
GlobalUnlock(handle->hglobal);
}
+static ULONG handle_read(struct handle_wrapper *handle, ULONG *pos, void *dest, ULONG len)
+{
+ void *source;
+
+ EnterCriticalSection(&handle->lock);
+
+ if (*pos < handle->size)
+ len = min(handle->size - *pos, len);
+ else
+ len = 0;
+
+ source = GlobalLock(handle->hglobal);
+ if (source)
+ {
+ memcpy(dest, (char *)source + *pos, len);
+ *pos += len;
+ GlobalUnlock(handle->hglobal);
+ }
+ else
+ {
+ WARN("read from invalid hglobal %p\n", handle->hglobal);
+ len = 0;
+ }
+
+ LeaveCriticalSection(&handle->lock);
+ return len;
+}
+
static HGLOBAL handle_gethglobal(struct handle_wrapper *handle)
{
return handle->hglobal;
@@ -222,59 +250,12 @@ static HRESULT WINAPI HGLOBALStreamImpl_Read(
ULONG* pcbRead) /* [out] */
{
HGLOBALStreamImpl* This = impl_from_IStream(iface);
+ ULONG num_bytes;
- void* supportBuffer;
- ULONG bytesReadBuffer;
- ULONG bytesToReadFromBuffer;
-
- TRACE("(%p, %p, %d, %p)\n", iface,
- pv, cb, pcbRead);
-
- /*
- * If the caller is not interested in the number of bytes read,
- * we use another buffer to avoid "if" statements in the code.
- */
- if (pcbRead==0)
- pcbRead = &bytesReadBuffer;
-
- /*
- * Using the known size of the stream, calculate the number of bytes
- * to read from the block chain
- */
- bytesToReadFromBuffer = min( handle_getsize(This->handle) - This->currentPosition.u.LowPart, cb);
-
- /*
- * Lock the buffer in position and copy the data.
- */
- supportBuffer = handle_lock(This->handle);
- if (!supportBuffer)
- {
- WARN("read from invalid hglobal %p\n", handle_gethglobal(This->handle));
- *pcbRead = 0;
- return S_OK;
- }
-
- memcpy(pv, (char *) supportBuffer+This->currentPosition.u.LowPart, bytesToReadFromBuffer);
-
- /*
- * Move the current position to the new position
- */
- This->currentPosition.u.LowPart+=bytesToReadFromBuffer;
-
- /*
- * Return the number of bytes read.
- */
- *pcbRead = bytesToReadFromBuffer;
-
- /*
- * Cleanup
- */
- handle_unlock(This->handle);
+ TRACE("(%p, %p, %d, %p)\n", iface, pv, cb, pcbRead);
- /*
- * Always returns S_OK even if the end of the stream is reached before the
- * buffer is filled
- */
+ num_bytes = handle_read(This->handle, &This->currentPosition.u.LowPart, pv, cb);
+ if (pcbRead) *pcbRead = num_bytes;
return S_OK;
}
--
2.7.0

View File

@ -0,0 +1,158 @@
From 8656639654762bd3151593fe00a0c47080a085d8 Mon Sep 17 00:00:00 2001
From: Sebastian Lackner <sebastian@fds-team.de>
Date: Sat, 6 Feb 2016 04:09:45 +0100
Subject: ole32: Improve thread-safety of HGLOBALStreamImpl_Write.
---
dlls/ole32/hglobalstream.c | 117 ++++++++++++++++++---------------------------
1 file changed, 46 insertions(+), 71 deletions(-)
diff --git a/dlls/ole32/hglobalstream.c b/dlls/ole32/hglobalstream.c
index 27ac706..2c08710 100644
--- a/dlls/ole32/hglobalstream.c
+++ b/dlls/ole32/hglobalstream.c
@@ -77,16 +77,6 @@ static void handle_release(struct handle_wrapper *handle)
}
}
-static void *handle_lock(struct handle_wrapper *handle)
-{
- return GlobalLock(handle->hglobal);
-}
-
-static void handle_unlock(struct handle_wrapper *handle)
-{
- GlobalUnlock(handle->hglobal);
-}
-
static ULONG handle_read(struct handle_wrapper *handle, ULONG *pos, void *dest, ULONG len)
{
void *source;
@@ -115,6 +105,48 @@ static ULONG handle_read(struct handle_wrapper *handle, ULONG *pos, void *dest,
return len;
}
+static ULONG handle_write(struct handle_wrapper *handle, ULONG *pos, const void *source, ULONG len)
+{
+ void *dest;
+
+ if (!len)
+ return 0;
+
+ EnterCriticalSection(&handle->lock);
+
+ if (*pos + len > handle->size)
+ {
+ HGLOBAL hglobal = GlobalReAlloc(handle->hglobal, *pos + len, GMEM_MOVEABLE);
+ if (hglobal)
+ {
+ handle->hglobal = hglobal;
+ handle->size = *pos + len;
+ }
+ else
+ {
+ len = 0;
+ goto done;
+ }
+ }
+
+ dest = GlobalLock(handle->hglobal);
+ if (dest)
+ {
+ memcpy((char *)dest + *pos, source, len);
+ *pos += len;
+ GlobalUnlock(handle->hglobal);
+ }
+ else
+ {
+ WARN("write to invalid hglobal %p\n", handle->hglobal);
+ /* len = 0; */
+ }
+
+done:
+ LeaveCriticalSection(&handle->lock);
+ return len;
+}
+
static HGLOBAL handle_gethglobal(struct handle_wrapper *handle)
{
return handle->hglobal;
@@ -277,71 +309,14 @@ static HRESULT WINAPI HGLOBALStreamImpl_Write(
ULONG* pcbWritten) /* [out] */
{
HGLOBALStreamImpl* This = impl_from_IStream(iface);
-
- void* supportBuffer;
- ULARGE_INTEGER newSize;
- ULONG bytesWritten = 0;
+ ULONG num_bytes;
TRACE("(%p, %p, %d, %p)\n", iface, pv, cb, pcbWritten);
- /*
- * If the caller is not interested in the number of bytes written,
- * we use another buffer to avoid "if" statements in the code.
- */
- if (pcbWritten == 0)
- pcbWritten = &bytesWritten;
-
- if (cb == 0)
- goto out;
-
- *pcbWritten = 0;
-
- newSize.u.HighPart = 0;
- newSize.u.LowPart = This->currentPosition.u.LowPart + cb;
-
- /*
- * Verify if we need to grow the stream
- */
- if (newSize.u.LowPart > handle_getsize(This->handle))
- {
- /* grow stream */
- HRESULT hr = IStream_SetSize(iface, newSize);
- if (FAILED(hr))
- {
- ERR("IStream_SetSize failed with error 0x%08x\n", hr);
- return hr;
- }
- }
-
- /*
- * Lock the buffer in position and copy the data.
- */
- supportBuffer = handle_lock(This->handle);
- if (!supportBuffer)
- {
- WARN("write to invalid hglobal %p\n", handle_gethglobal(This->handle));
- return S_OK;
- }
-
- memcpy((char *) supportBuffer+This->currentPosition.u.LowPart, pv, cb);
-
- /*
- * Move the current position to the new position
- */
- This->currentPosition.u.LowPart+=cb;
-
- /*
- * Cleanup
- */
- handle_unlock(This->handle);
-
-out:
- /*
- * Return the number of bytes read.
- */
- *pcbWritten = cb;
+ num_bytes = handle_write(This->handle, &This->currentPosition.u.LowPart, pv, cb);
+ if (pcbWritten) *pcbWritten = num_bytes;
- return S_OK;
+ return (num_bytes < cb) ? E_OUTOFMEMORY : S_OK;
}
/***
--
2.7.0

View File

@ -0,0 +1 @@
Fixes: Implement proper refcounting and locking for HGLOBAL based IStream

View File

@ -238,6 +238,7 @@ patch_enable_all ()
enable_nvcuvid_CUDA_Video_Support="$1"
enable_nvencodeapi_Video_Encoder="$1"
enable_ole32_CoGetApartmentType="$1"
enable_ole32_HGLOBALStream="$1"
enable_oleaut32_CreateTypeLib="$1"
enable_oleaut32_TKIND_COCLASS="$1"
enable_oleaut32_x86_64_Marshaller="$1"
@ -853,6 +854,9 @@ patch_enable ()
ole32-CoGetApartmentType)
enable_ole32_CoGetApartmentType="$2"
;;
ole32-HGLOBALStream)
enable_ole32_HGLOBALStream="$2"
;;
oleaut32-CreateTypeLib)
enable_oleaut32_CreateTypeLib="$2"
;;
@ -5202,6 +5206,30 @@ if test "$enable_nvencodeapi_Video_Encoder" -eq 1; then
) >> "$patchlist"
fi
# Patchset ole32-HGLOBALStream
# |
# | Modified files:
# | * dlls/ole32/hglobalstream.c, dlls/ole32/tests/hglobalstream.c
# |
if test "$enable_ole32_HGLOBALStream" -eq 1; then
patch_apply ole32-HGLOBALStream/0001-ole32-tests-Add-a-bunch-of-tests-for-HGLOBAL-based-I.patch
patch_apply ole32-HGLOBALStream/0002-ole32-Add-a-check-for-hglobal-pointer-to-GetHGlobalF.patch
patch_apply ole32-HGLOBALStream/0003-ole32-Add-a-wrapper-for-memory-block-managed-by-HGLO.patch
patch_apply ole32-HGLOBALStream/0004-ole32-Set-DebugInfo-Spare-0-for-handle_wrapper-lock.patch
patch_apply ole32-HGLOBALStream/0005-ole32-Allow-moving-a-being-reallocated-block-of-memo.patch
patch_apply ole32-HGLOBALStream/0006-ole32-Improve-thread-safety-of-HGLOBALStreamImpl_Rea.patch
patch_apply ole32-HGLOBALStream/0007-ole32-Improve-thread-safety-of-HGLOBALStreamImpl_Wri.patch
(
echo '+ { "Dmitry Timoshkov", "ole32/tests: Add a bunch of tests for HGLOBAL based IStream::Clone.", 1 },';
echo '+ { "Dmitry Timoshkov", "ole32: Add a check for hglobal pointer to GetHGlobalFromStream.", 1 },';
echo '+ { "Dmitry Timoshkov", "ole32: Add a wrapper for memory block managed by HGLOBAL based IStream.", 1 },';
echo '+ { "Sebastian Lackner", "ole32: Set DebugInfo->Spare[0] for handle_wrapper lock.", 1 },';
echo '+ { "Dmitry Timoshkov", "ole32: Allow moving a being reallocated block of memory managed by HGLOBAL based IStream.", 1 },';
echo '+ { "Sebastian Lackner", "ole32: Improve thread-safety of HGLOBALStreamImpl_Read.", 1 },';
echo '+ { "Sebastian Lackner", "ole32: Improve thread-safety of HGLOBALStreamImpl_Write.", 1 },';
) >> "$patchlist"
fi
# Patchset oleaut32-CreateTypeLib
# |
# | This patchset fixes the following Wine bugs: