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

Rémi Bernon rbernon at codeweavers.com
Tue Jan 4 14:31:10 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>
---

v2: Use an even more ad-hoc flag to detect LockRequest calls during
    ReportData.

 dlls/mshtml/tests/htmldoc.c | 18 +++++++++++++++++-
 dlls/urlmon/binding.c       | 23 +++++++++++++++++++++++
 2 files changed, 40 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 a74970033b3..56c479d6d2f 100644
--- a/dlls/urlmon/binding.c
+++ b/dlls/urlmon/binding.c
@@ -64,6 +64,8 @@ typedef enum {
 #define BINDING_STOPPED   0x0002
 #define BINDING_OBJAVAIL  0x0004
 #define BINDING_ABORTED   0x0008
+#define BINDING_LOCKING   0x0010
+#define BINDING_TERMINATE 0x0020
 
 typedef struct {
     IBinding              IBinding_iface;
@@ -1126,7 +1128,9 @@ static void report_data(Binding *This, DWORD bscf, ULONG progress, ULONG progres
         HRESULT hres;
 
         if(!(This->state & BINDING_LOCKED)) {
+            This->state |= BINDING_LOCKING;
             hres = IInternetProtocolEx_LockRequest(&This->protocol->IInternetProtocolEx_iface, 0);
+            This->state &= ~BINDING_LOCKING;
             if(SUCCEEDED(hres))
                 This->state |= BINDING_LOCKED;
         }
@@ -1142,6 +1146,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;
@@ -1177,6 +1188,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_LOCKING) {
+        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