[2/3] advapi32: Fix EnumServicesStatusEx on Wow64.
Hans Leidekker
hans at codeweavers.com
Tue Nov 7 07:10:40 CST 2017
Signed-off-by: Hans Leidekker <hans at codeweavers.com>
---
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 79d069336e..78491aed13 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 b7d1e92718..7c5df7d961 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 7215504e82..a1d3c9e6cc 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++;
}
}
--
2.11.0
More information about the wine-patches
mailing list