Hans Leidekker : advapi32: Fix EnumServicesStatusEx on Wow64.

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


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

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

advapi32: Fix EnumServicesStatusEx on Wow64.

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

---

 dlls/advapi32/service.c | 82 +++++++++++++++++++++++++++++++++++++++++--------
 include/wine/svcctl.idl |  7 +++++
 programs/services/rpc.c | 16 +++++-----
 3 files changed, 84 insertions(+), 21 deletions(-)

diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c
index 79d0693..78491ae 100644
--- a/dlls/advapi32/service.c
+++ b/dlls/advapi32/service.c
@@ -1893,9 +1893,11 @@ EnumServicesStatusExW( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD st
                        LPBYTE buffer, DWORD size, LPDWORD needed, LPDWORD returned,
                        LPDWORD resume_handle, LPCWSTR group )
 {
-    DWORD err, i;
-    ENUM_SERVICE_STATUS_PROCESSW dummy_status;
+    DWORD err, i, offset, buflen, count, total_size = 0;
     ENUM_SERVICE_STATUS_PROCESSW *services = (ENUM_SERVICE_STATUS_PROCESSW *)buffer;
+    struct enum_service_status_process *entry;
+    const WCHAR *str;
+    BYTE *buf;
 
     TRACE("%p %u 0x%x 0x%x %p %u %p %p %p %s\n", hmngr, level, type, state, buffer,
           size, needed, returned, resume_handle, debugstr_w(group));
@@ -1910,18 +1912,24 @@ EnumServicesStatusExW( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD st
         SetLastError( ERROR_INVALID_HANDLE );
         return FALSE;
     }
+    if (!needed || !returned)
+    {
+        SetLastError( ERROR_INVALID_ADDRESS );
+        return FALSE;
+    }
 
-    /* make sure we pass a valid buffer pointer */
-    if (!services || size < sizeof(*services))
+    /* make sure we pass a valid pointer */
+    buflen = max( size, sizeof(*services) );
+    if (!(buf = heap_alloc( buflen )))
     {
-        buffer = (BYTE *)&dummy_status;
-        size = sizeof(dummy_status);
+        SetLastError( ERROR_NOT_ENOUGH_MEMORY );
+        return FALSE;
     }
 
     __TRY
     {
-        err = svcctl_EnumServicesStatusExW( hmngr, SC_ENUM_PROCESS_INFO, type, state, buffer, size, needed,
-                                            returned, resume_handle, group );
+        err = svcctl_EnumServicesStatusExW( hmngr, SC_ENUM_PROCESS_INFO, type, state, buf, buflen, needed,
+                                            &count, resume_handle, group );
     }
     __EXCEPT(rpc_filter)
     {
@@ -1929,20 +1937,68 @@ EnumServicesStatusExW( SC_HANDLE hmngr, SC_ENUM_TYPE level, DWORD type, DWORD st
     }
     __ENDTRY
 
+    *returned = 0;
     if (err != ERROR_SUCCESS)
     {
+        /* double the needed size to fit the potentially larger ENUM_SERVICE_STATUS_PROCESSW */
+        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_process *)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_process *)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].ServiceStatusProcess = entry->service_status_process;
+        entry++;
+    }
+
+    heap_free( buf );
+    *needed = 0;
+    *returned = count;
     return TRUE;
 }
 
diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl
index b7d1e92..7c5df7d 100644
--- a/include/wine/svcctl.idl
+++ b/include/wine/svcctl.idl
@@ -218,6 +218,13 @@ struct enum_service_status
     SERVICE_STATUS service_status;
 };
 
+struct enum_service_status_process
+{
+    DWORD                  service_name;
+    DWORD                  display_name;
+    SERVICE_STATUS_PROCESS service_status_process;
+};
+
 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 7215504..a1d3c9e 100644
--- a/programs/services/rpc.c
+++ b/programs/services/rpc.c
@@ -1461,7 +1461,7 @@ DWORD __cdecl svcctl_EnumServicesStatusExW(
     DWORD_PTR offset;
     struct sc_manager_handle *manager;
     struct service_entry *service;
-    ENUM_SERVICE_STATUS_PROCESSW *s;
+    struct enum_service_status_process *s;
 
     WINE_TRACE("(%p, 0x%x, 0x%x, %p, %u, %p, %p, %s)\n", hmngr, type, state, buffer, size,
                needed, returned, wine_dbgstr_w(group));
@@ -1489,7 +1489,7 @@ DWORD __cdecl svcctl_EnumServicesStatusExW(
         if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state)
             && match_group(service->config.lpLoadOrderGroup, group))
         {
-            total_size += sizeof(ENUM_SERVICE_STATUS_PROCESSW);
+            total_size += sizeof(*s);
             total_size += (strlenW(service->name) + 1) * sizeof(WCHAR);
             if (service->config.lpDisplayName)
             {
@@ -1505,8 +1505,8 @@ DWORD __cdecl svcctl_EnumServicesStatusExW(
         scmdatabase_unlock(manager->db);
         return ERROR_MORE_DATA;
     }
-    s = (ENUM_SERVICE_STATUS_PROCESSW *)buffer;
-    offset = num_services * sizeof(ENUM_SERVICE_STATUS_PROCESSW);
+    s = (struct enum_service_status_process *)buffer;
+    offset = num_services * sizeof(*s);
     LIST_FOR_EACH_ENTRY(service, &manager->db->services, struct service_entry, entry)
     {
         if ((service->status.dwServiceType & type) && map_state(service->status.dwCurrentState, state)
@@ -1514,18 +1514,18 @@ DWORD __cdecl svcctl_EnumServicesStatusExW(
         {
             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;
             }
-            fill_status_process(&s->ServiceStatusProcess, service);
+            fill_status_process(&s->service_status_process, service);
             s++;
         }
     }




More information about the wine-cvs mailing list