Commit ac0744d4 authored by Hans Leidekker's avatar Hans Leidekker Committed by Alexandre Julliard

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: 's avatarHans Leidekker <hans@codeweavers.com> Signed-off-by: 's avatarAlexandre Julliard <julliard@winehq.org>
parent 06d9c7e2
......@@ -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;
}
......
......@@ -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;
......
......@@ -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++;
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment