diff --git a/dlls/advapi32/service.c b/dlls/advapi32/service.c index 78491aed134..3f2fb93d68a 100644 --- a/dlls/advapi32/service.c +++ b/dlls/advapi32/service.c @@ -1543,6 +1543,8 @@ BOOL WINAPI QueryServiceConfig2A(SC_HANDLE hService, DWORD dwLevel, LPBYTE buffe BOOL ret; LPBYTE bufferW = NULL; + TRACE("%p %u %p %u %p\n", hService, dwLevel, buffer, size, needed); + if(buffer && size) bufferW = heap_alloc(size); @@ -1591,24 +1593,42 @@ cleanup: BOOL WINAPI QueryServiceConfig2W(SC_HANDLE hService, DWORD dwLevel, LPBYTE buffer, DWORD size, LPDWORD needed) { + BYTE *bufptr; DWORD err; - if(dwLevel!=SERVICE_CONFIG_DESCRIPTION && dwLevel!=SERVICE_CONFIG_PRESHUTDOWN_INFO) { + TRACE("%p %u %p %u %p\n", hService, dwLevel, buffer, size, needed); + + if (!buffer && size) + { + SetLastError(ERROR_INVALID_ADDRESS); + return FALSE; + } + + switch (dwLevel) + { + case SERVICE_CONFIG_DESCRIPTION: + if (!(bufptr = heap_alloc( size ))) return ERROR_OUTOFMEMORY; + break; + + case SERVICE_CONFIG_PRESHUTDOWN_INFO: + bufptr = buffer; + break; + + default: FIXME("Level %d not implemented\n", dwLevel); SetLastError(ERROR_INVALID_LEVEL); return FALSE; } - if(!buffer && size) { + if (!needed) + { SetLastError(ERROR_INVALID_ADDRESS); return FALSE; } - TRACE("%p 0x%d %p 0x%d %p\n", hService, dwLevel, buffer, size, needed); - __TRY { - err = svcctl_QueryServiceConfig2W(hService, dwLevel, buffer, size, needed); + err = svcctl_QueryServiceConfig2W(hService, dwLevel, bufptr, size, needed); } __EXCEPT(rpc_filter) { @@ -1616,22 +1636,53 @@ BOOL WINAPI QueryServiceConfig2W(SC_HANDLE hService, DWORD dwLevel, LPBYTE buffe } __ENDTRY - if (err != ERROR_SUCCESS) - { - SetLastError( err ); - return FALSE; - } - switch (dwLevel) { case SERVICE_CONFIG_DESCRIPTION: - if (buffer) + { + SERVICE_DESCRIPTIONW *desc = (SERVICE_DESCRIPTIONW *)buffer; + struct service_description *s = (struct service_description *)bufptr; + + if (err != ERROR_SUCCESS && err != ERROR_INSUFFICIENT_BUFFER) { - SERVICE_DESCRIPTIONW *descr = (SERVICE_DESCRIPTIONW *)buffer; - if (descr->lpDescription) /* make it an absolute pointer */ - descr->lpDescription = (WCHAR *)(buffer + (ULONG_PTR)descr->lpDescription); - break; + heap_free( bufptr ); + SetLastError( err ); + return FALSE; } + + /* adjust for potentially larger SERVICE_DESCRIPTIONW structure */ + if (*needed == sizeof(*s)) *needed = sizeof(*desc); + else + *needed = *needed - FIELD_OFFSET(struct service_description, description) + sizeof(*desc); + + if (size < *needed) + { + heap_free( bufptr ); + SetLastError( ERROR_INSUFFICIENT_BUFFER ); + return FALSE; + } + if (desc) + { + if (!s->size) desc->lpDescription = NULL; + else + { + desc->lpDescription = (WCHAR *)(desc + 1); + memcpy( desc->lpDescription, s->description, s->size ); + } + } + heap_free( bufptr ); + break; + } + case SERVICE_CONFIG_PRESHUTDOWN_INFO: + if (err != ERROR_SUCCESS) + { + SetLastError( err ); + return FALSE; + } + break; + + default: + break; } return TRUE; diff --git a/include/wine/svcctl.idl b/include/wine/svcctl.idl index 7c5df7d9614..c14cd6bb50c 100644 --- a/include/wine/svcctl.idl +++ b/include/wine/svcctl.idl @@ -225,6 +225,12 @@ struct enum_service_status_process SERVICE_STATUS_PROCESS service_status_process; }; +struct service_description +{ + USHORT size; + WCHAR description[1]; +}; + 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 a1d3c9e6ccc..20c5a2761f3 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -879,27 +879,32 @@ DWORD __cdecl svcctl_QueryServiceConfig2W( SC_RPC_HANDLE hService, DWORD level, switch (level) { case SERVICE_CONFIG_DESCRIPTION: - { - SERVICE_DESCRIPTIONW *descr = (SERVICE_DESCRIPTIONW *)buffer; + { + struct service_description *desc = (struct service_description *)buffer; + DWORD total_size = sizeof(*desc); - service_lock(service->service_entry); - *needed = sizeof(*descr); + service_lock(service->service_entry); + if (service->service_entry->description) + total_size += strlenW(service->service_entry->description) * sizeof(WCHAR); + + *needed = total_size; + if (size >= total_size) + { if (service->service_entry->description) - *needed += (strlenW(service->service_entry->description) + 1) * sizeof(WCHAR); - if (size >= *needed) { - if (service->service_entry->description) - { - /* store a buffer offset instead of a pointer */ - descr->lpDescription = (WCHAR *)((BYTE *)(descr + 1) - buffer); - strcpyW( (WCHAR *)(descr + 1), service->service_entry->description ); - } - else descr->lpDescription = NULL; + strcpyW( desc->description, service->service_entry->description ); + desc->size = total_size - FIELD_OFFSET(struct service_description, description); + } + else + { + desc->description[0] = 0; + desc->size = 0; } - else err = ERROR_INSUFFICIENT_BUFFER; - service_unlock(service->service_entry); } - break; + else err = ERROR_INSUFFICIENT_BUFFER; + service_unlock(service->service_entry); + } + break; case SERVICE_CONFIG_PRESHUTDOWN_INFO: service_lock(service->service_entry);