[PATCH 2/2] urlmon: Always report available data before stop_binding.

Rémi Bernon rbernon at codeweavers.com
Tue Jan 4 11:10:01 CST 2022


The HTML launcher of several Rebellion games (all probably based on the
same code) use a custom protocol handler which calls ReportResult in
LockRequest, causing an invalid memory access when OnDataAvailable
callback is called, as the binding has already been stopped and
terminated.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=46213
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52286
Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
---
 dlls/mshtml/tests/htmldoc.c | 18 +++++++++++++++++-
 dlls/urlmon/binding.c       | 20 ++++++++++++++++++++
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/dlls/mshtml/tests/htmldoc.c b/dlls/mshtml/tests/htmldoc.c
index 89311c5d8c8..f8df7d7736d 100644
--- a/dlls/mshtml/tests/htmldoc.c
+++ b/dlls/mshtml/tests/htmldoc.c
@@ -522,6 +522,8 @@ static const IDispatchVtbl ExternalVtbl = {
 
 static IDispatch External = { &ExternalVtbl };
 
+static IInternetProtocolSink *protocol_sink;
+
 static HRESULT WINAPI Protocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
 {
     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocol, riid)) {
@@ -556,6 +558,8 @@ static HRESULT WINAPI Protocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
 
     CHECK_EXPECT(Start);
 
+    protocol_sink = pOIProtSink;
+
     ok(pOIProtSink != NULL, "pOIProtSink == NULL\n");
     ok(pOIBindInfo != NULL, "pOIBindInfo == NULL\n");
     ok(!grfPI, "grfPI = %x\n", grfPI);
@@ -619,7 +623,9 @@ static HRESULT WINAPI Protocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
     ok(hres == S_OK, "ReportData failed: %08x\n", hres);
 
     hres = IInternetProtocolSink_ReportResult(pOIProtSink, S_OK, 0, NULL);
-    ok(hres == S_OK, "ReportResult failed: %08x\n", hres);
+    ok(hres == E_FAIL, "ReportResult failed: %08x\n", hres);
+
+    protocol_sink = NULL;
 
     return S_OK;
 }
@@ -689,7 +695,17 @@ static HRESULT WINAPI Protocol_Seek(IInternetProtocol *iface,
 
 static HRESULT WINAPI Protocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
 {
+    HRESULT hres;
+
     CHECK_EXPECT(LockRequest);
+
+    if(protocol_sink) {
+        hres = IInternetProtocolSink_ReportResult(protocol_sink, S_OK, 0, NULL);
+        ok(hres == S_OK, "ReportResult failed: %08x\n", hres);
+        ok(!called_UnlockRequest, "unexpected UnlockRequest\n");
+        ok(!called_Terminate, "unexpected Terminate\n");
+    }
+
     return S_OK;
 }
 
diff --git a/dlls/urlmon/binding.c b/dlls/urlmon/binding.c
index b7bea04c4d1..7e8d33fdf97 100644
--- a/dlls/urlmon/binding.c
+++ b/dlls/urlmon/binding.c
@@ -64,6 +64,7 @@ typedef enum {
 #define BINDING_STOPPED   0x0002
 #define BINDING_OBJAVAIL  0x0004
 #define BINDING_ABORTED   0x0008
+#define BINDING_TERMINATE 0x0010
 
 typedef struct {
     IBinding              IBinding_iface;
@@ -1143,6 +1144,13 @@ static void report_data(Binding *This, DWORD bscf, ULONG progress, ULONG progres
 
         hres = IBindStatusCallback_OnDataAvailable(This->callback, bscf, progress,
                 &formatetc, &stgmed);
+
+        if(This->state & BINDING_TERMINATE) {
+            stop_binding(This, This->hres, NULL);
+            IInternetProtocolEx_Terminate(&This->protocol->IInternetProtocolEx_iface, 0);
+            return;
+        }
+
         if(hres != S_OK) {
             if(This->download_state != END_DOWNLOAD) {
                 This->download_state = END_DOWNLOAD;
@@ -1178,6 +1186,18 @@ static HRESULT WINAPI InternetProtocolSink_ReportResult(IInternetProtocolSink *i
 
     TRACE("(%p)->(%08x %d %s)\n", This, hrResult, dwError, debugstr_w(szResult));
 
+    /* Make sure we don't call stop_binding before available data has been reported,
+       as some custom protocol handlers (in Zombie Army Trilogy launcher and similar)
+       call ReportResult within LockRequest, in report_data, before OnDataAvailable
+       has been called. Stopping or terminating the binding there causes an invalid
+       memory access later.
+    */
+    if(This->state & BINDING_LOCKED) {
+        This->hres = hrResult;
+        This->state |= BINDING_TERMINATE;
+        return S_OK;
+    }
+
     stop_binding(This, hrResult, szResult);
 
     IInternetProtocolEx_Terminate(&This->protocol->IInternetProtocolEx_iface, 0);
-- 
2.34.1




More information about the wine-devel mailing list