Hans Leidekker : advapi32: Fix EnumServicesStatus on Wow64.

Alexandre Julliard julliard at winehq.org
Wed Nov 8 16:01:10 CST 2017


Module: wine
Branch: master
Commit: ac0744d4505a583de2cbdc6082f2726393c797df
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=ac0744d4505a583de2cbdc6082f2726393c797df

Author: Hans Leidekker <hans at codeweavers.com>
Date:   Tue Nov  7 14:10:39 2017 +0100

advapi32: Fix EnumServicesStatus on Wow64.

The structures returned by this function contain pointers, which breaks on Wow64 if
the client is 32-bit (the service manager always runs in a 64-bit process).

This patch introduces a variant of ENUM_SERVICE_STATUS with offsets instead of pointers
and converts the structures on the client side.

The downside is that we need to buffer the data, but in return we can get rid of the
dummy buffer pointer.

Signed-off-by: Hans Leidekker <hans at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/advapi32/service.c | 89 +++++++++++++++++++++++++++++++++++++++++++------
 include/wine/svcctl.idl |  8 +++++
 programs/services/rpc.c | 19 +++++------
 3 files changed, 95 insertions(+), 21 deletions(-)

diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index fa6ebfe..79d0693 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -1654,6 +1654,17 @@ EnumServicesStatusA( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_ST
     TRACE("%p 0x%x 0x%x %p %u %p %p %p\n", hmngr, type, state, services, size, needed,
           returned, resume_handle);
 
+    if (!hmngr)
+    {
+        SetLastError( ERROR_INVALID_HANDLE );
+        return FALSE;
+    }
+    if (!needed || !returned)
+    {
+        SetLastError( ERROR_INVALID_ADDRESS );
+        return FALSE;
+    }
+
     sz = max( 2 * size, sizeof(*servicesW) );
     if (!(servicesW = heap_alloc( sz )))
     {
@@ -1701,8 +1712,10 @@ EnumServicesStatusW( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_ST
                      services, DWORD size, LPDWORD needed, LPDWORD returned,
                      LPDWORD resume_handle )
 {
-    DWORD err, i;
-    ENUM_SERVICE_STATUSW dummy_status;
+    DWORD err, i, offset, buflen, count, total_size = 0;
+    struct enum_service_status *entry;
+    const WCHAR *str;
+    BYTE *buf;
 
     TRACE("%p 0x%x 0x%x %p %u %p %p %p\n", hmngr, type, state, services, size, needed,
           returned, resume_handle);
@@ -1712,17 +1725,23 @@ EnumServicesStatusW( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_ST
         SetLastError( ERROR_INVALID_HANDLE );
         return FALSE;
     }
+    if (!needed || !returned)
+    {
+        SetLastError( ERROR_INVALID_ADDRESS );
+        return FALSE;
+    }
 
     /* make sure we pass a valid pointer */
-    if (!services || size < sizeof(*services))
+    buflen = max( size, sizeof(*services) );
+    if (!(buf = heap_alloc( buflen )))
     {
-        services = &dummy_status;
-        size = sizeof(dummy_status);
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return FALSE;
     }
 
     __TRY
     {
-        err = svcctl_EnumServicesStatusW( hmngr, type, state, (BYTE *)services, size, needed, returned, resume_handle );
+        err = svcctl_EnumServicesStatusW( hmngr, type, state, buf, buflen, needed, &count, resume_handle );
     }
     __EXCEPT(rpc_filter)
     {
@@ -1730,20 +1749,68 @@ EnumServicesStatusW( SC_HANDLE hmngr, DWORD type, DWORD state, LPENUM_SERVICE_ST
     }
     __ENDTRY
 
+    *returned = 0;
     if (err != ERROR_SUCCESS)
     {
+        /* double the needed size to fit the potentially larger ENUM_SERVICE_STATUSW */
+        if (err == ERROR_MORE_DATA) *needed *= 2;
+        heap_free( buf );
         SetLastError( err );
         return FALSE;
     }
 
-    for (i = 0; i < *returned; i++)
+    entry = (struct enum_service_status *)buf;
+    for (i = 0; i < count; i++)
     {
-        /* convert buffer offsets into pointers */
-        services[i].lpServiceName = (WCHAR *)((char *)services + (DWORD_PTR)services[i].lpServiceName);
-        if (services[i].lpDisplayName)
-            services[i].lpDisplayName = (WCHAR *)((char *)services + (DWORD_PTR)services[i].lpDisplayName);
+        total_size += sizeof(*services);
+        if (entry->service_name)
+        {
+            str = (const WCHAR *)(buf + entry->service_name);
+            total_size += (strlenW( str ) + 1) * sizeof(WCHAR);
+        }
+        if (entry->display_name)
+        {
+            str = (const WCHAR *)(buf + entry->display_name);
+            total_size += (strlenW( str ) + 1) * sizeof(WCHAR);
+        }
+        entry++;
+    }
+
+    if (total_size > size)
+    {
+        heap_free( buf );
+        *needed = total_size;
+        SetLastError( ERROR_MORE_DATA );
+        return FALSE;
+    }
+
+    offset = count * sizeof(*services);
+    entry = (struct enum_service_status *)buf;
+    for (i = 0; i < count; i++)
+    {
+        DWORD str_size;
+        str = (const WCHAR *)(buf + entry->service_name);
+        str_size = (strlenW( str ) + 1) * sizeof(WCHAR);
+        services[i].lpServiceName = (WCHAR *)((char *)services + offset);
+        memcpy( services[i].lpServiceName, str, str_size );
+        offset += str_size;
+
+        if (!entry->display_name) services[i].lpDisplayName = NULL;
+        else
+        {
+            str = (const WCHAR *)(buf + entry->display_name);
+            str_size = (strlenW( str ) + 1) * sizeof(WCHAR);
+            services[i].lpDisplayName = (WCHAR *)((char *)services + offset);
+            memcpy( services[i].lpDisplayName, str, str_size );
+            offset += str_size;
+        }
+        services[i].ServiceStatus = entry->service_status;
+        entry++;
     }
 
+    heap_free( buf );
+    *needed = 0;
+    *returned = count;
     return TRUE;
 }
 
diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl
index 9fd5ca4..b7d1e92 100644
--- a/include/wine/svcctl.idl
+++ b/include/wine/svcctl.idl
@@ -210,6 +210,14 @@ typedef enum _SC_ENUM_TYPE {
 
 cpp_quote("#endif")
 
+/* internal version of ENUM_SERVICE_STATUSA/W that doesn't depend on pointer size */
+struct enum_service_status
+{
+    DWORD          service_name;
+    DWORD          display_name;
+    SERVICE_STATUS service_status;
+};
+
 typedef struct _SERVICE_RPC_REQUIRED_PRIVILEGES_INFO {
     DWORD cbRequiredPrivileges;
     [size_is(cbRequiredPrivileges)] BYTE *pRequiredPrivileges;
diff --git a/programs/services/rpc.c b/programs/services/rpc.c
index 4435af9..7215504 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -1342,11 +1342,10 @@ DWORD __cdecl svcctl_EnumServicesStatusW(
     LPDWORD returned,
     LPDWORD resume)
 {
-    DWORD err, sz, total_size, num_services;
-    DWORD_PTR offset;
+    DWORD err, sz, total_size, num_services, offset;
     struct sc_manager_handle *manager;
     struct service_entry *service;
-    ENUM_SERVICE_STATUSW *s;
+    struct enum_service_status *s;
 
     WINE_TRACE("(%p, 0x%x, 0x%x, %p, %u, %p, %p, %p)\n", hmngr, type, state, buffer, size, needed, returned, resume);
 
@@ -1366,7 +1365,7 @@ DWORD __cdecl svcctl_EnumServicesStatusW(
     {
         if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state))
         {
-            total_size += sizeof(ENUM_SERVICE_STATUSW);
+            total_size += sizeof(*s);
             total_size += (strlenW(service->name) + 1) * sizeof(WCHAR);
             if (service->config.lpDisplayName)
             {
@@ -1382,26 +1381,26 @@ DWORD __cdecl svcctl_EnumServicesStatusW(
         scmdatabase_unlock(manager->db);
         return ERROR_MORE_DATA;
     }
-    s = (ENUM_SERVICE_STATUSW *)buffer;
-    offset = num_services * sizeof(ENUM_SERVICE_STATUSW);
+    s = (struct enum_service_status *)buffer;
+    offset = num_services * sizeof(struct enum_service_status);
     LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
     {
         if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state))
         {
             sz = (strlenW(service->name) + 1) * sizeof(WCHAR);
             memcpy(buffer + offset, service->name, sz);
-            s->lpServiceName = (WCHAR *)offset; /* store a buffer offset instead of a pointer */
+            s->service_name = offset;
             offset += sz;
 
-            if (!service->config.lpDisplayName) s->lpDisplayName = NULL;
+            if (!service->config.lpDisplayName) s->display_name = 0;
             else
             {
                 sz = (strlenW(service->config.lpDisplayName) + 1) * sizeof(WCHAR);
                 memcpy(buffer + offset, service->config.lpDisplayName, sz);
-                s->lpDisplayName = (WCHAR *)offset;
+                s->display_name = offset;
                 offset += sz;
             }
-            s->ServiceStatus = service->status;
+            s->service_status = service->status;
             s++;
         }
     }




More information about the wine-cvs mailing list