Module: wine Branch: master Commit: 445996d31ba5818eb9fe4e41130349b6730284a1 URL: http://source.winehq.org/git/wine.git/?a=commit;h=445996d31ba5818eb9fe4e4113...
Author: Hans Leidekker hans@codeweavers.com Date: Tue Nov 7 14:10:40 2017 +0100
advapi32: Fix EnumServicesStatusEx on Wow64.
Signed-off-by: Hans Leidekker hans@codeweavers.com Signed-off-by: Alexandre Julliard julliard@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++; } }