[1/3] advapi32: Fix EnumServicesStatus on Wow64.

Hans Leidekker hans at codeweavers.com
Tue Nov 7 07:10:39 CST 2017


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>
---
 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 fa6ebfec31..79d069336e 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 9fd5ca4c79..b7d1e92718 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 4435af935d..7215504e82 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++;
         }
     }
-- 
2.11.0




More information about the wine-patches mailing list