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: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
oldstable
Hans Leidekker 2017-11-07 14:10:39 +01:00 committed by Alexandre Julliard
parent 06d9c7e25e
commit ac0744d450
3 changed files with 95 additions and 21 deletions

View File

@ -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;
}

View File

@ -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;

View File

@ -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++;
}
}