[USERSRV] Hard-error improvements 5/7 - Implement STATUS_SERVICE_NOTIFICATION handling.

- In UserpCaptureStringParameters(), always capture the parameter
  strings with a terminating NULL. Also, no need to convert them to
  ANSI if they are used with a STATUS_SERVICE_NOTIFICATION message.
- Determine within UserpFormatMessages() the message box flags to be
  used, either from the message parameters (if STATUS_SERVICE_NOTIFICATION)
  or from the proposed response options and message status severity.
  These flags are then used by both UserpShowInformationBalloon()/UserpShellHardError()
  and by UserpMessageBox().
- Improve Message validation (especially for STATUS_SERVICE_NOTIFICATION).
- Try to display the hard error status number if it's an unknown one.
This commit is contained in:
Hermès Bélusca-Maïto 2018-03-19 02:22:20 +01:00
parent 9ea2783e56
commit 08d70697a3
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0

View file

@ -114,8 +114,8 @@ UserpCaptureStringParameters(
continue;
}
/* Allocate a buffer for the string */
TempStringU.MaximumLength = ParamStringU.Length;
/* Allocate a buffer for the string and reserve a NULL terminator */
TempStringU.MaximumLength = ParamStringU.Length + sizeof(UNICODE_NULL);
TempStringU.Length = ParamStringU.Length;
TempStringU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
@ -140,36 +140,47 @@ UserpCaptureStringParameters(
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
continue;
}
/* NULL-terminate the string */
TempStringU.Buffer[TempStringU.Length / sizeof(WCHAR)] = UNICODE_NULL;
DPRINT("ParamString = \'%wZ\'\n", &TempStringU);
/* Allocate a buffer for converted to ANSI string */
TempStringA.MaximumLength = RtlUnicodeStringToAnsiSize(&TempStringU);
TempStringA.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
TempStringA.MaximumLength);
if (!TempStringA.Buffer)
if (Message->Status == STATUS_SERVICE_NOTIFICATION)
{
/* We failed, skip this string */
DPRINT1("Cannot allocate memory with size %u, skipping.\n", TempStringA.MaximumLength);
/* Just keep the allocated NULL-terminated UNICODE string */
Parameters[nParam] = (ULONG_PTR)TempStringU.Buffer;
Size += TempStringU.Length;
}
else
{
/* Allocate a buffer for converted to ANSI string */
TempStringA.MaximumLength = RtlUnicodeStringToAnsiSize(&TempStringU);
TempStringA.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
TempStringA.MaximumLength);
if (!TempStringA.Buffer)
{
/* We failed, skip this string */
DPRINT1("Cannot allocate memory with size %u, skipping.\n", TempStringA.MaximumLength);
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
continue;
}
/* Convert string to ANSI and free temporary buffer */
Status = RtlUnicodeStringToAnsiString(&TempStringA, &TempStringU, FALSE);
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
continue;
}
if (!NT_SUCCESS(Status))
{
/* We failed, skip this string */
DPRINT1("RtlUnicodeStringToAnsiString() failed, Status 0x%lx, skipping.\n", Status);
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringA.Buffer);
continue;
}
/* Convert string to ANSI and free temporary buffer */
Status = RtlUnicodeStringToAnsiString(&TempStringA, &TempStringU, FALSE);
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
if (!NT_SUCCESS(Status))
{
/* We failed, skip this string */
DPRINT1("RtlUnicodeStringToAnsiString() failed, Status 0x%lx, skipping.\n", Status);
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringA.Buffer);
continue;
/* Note: RtlUnicodeStringToAnsiString() returns a NULL-terminated string */
Parameters[nParam] = (ULONG_PTR)TempStringA.Buffer;
Size += TempStringU.Length;
}
/* Note: RtlUnicodeStringToAnsiString returns NULL terminated string */
Parameters[nParam] = (ULONG_PTR)TempStringA.Buffer;
Size += TempStringU.Length;
}
else
{
@ -293,11 +304,48 @@ UserpGetClientFileName(
return STATUS_SUCCESS;
}
static
VOID
UserpDuplicateParamStringToUnicodeString(
IN OUT PUNICODE_STRING UnicodeString,
IN PCWSTR ParamString)
{
UNICODE_STRING FormatU, TempStringU;
/* Calculate buffer length for the text message */
RtlInitUnicodeString(&FormatU, (PWSTR)ParamString);
if (UnicodeString->MaximumLength < FormatU.MaximumLength)
{
/* Duplicate the text message in a larger buffer */
if (NT_SUCCESS(RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
&FormatU, &TempStringU)))
{
*UnicodeString = TempStringU;
}
else
{
/* We could not allocate a larger buffer; continue using the smaller original buffer */
DPRINT1("Cannot allocate memory for UnicodeString, use original buffer.\n");
/* Copy the truncated string, NULL-terminate it */
FormatU.MaximumLength = UnicodeString->MaximumLength;
FormatU.Length = FormatU.MaximumLength - sizeof(UNICODE_NULL);
RtlCopyUnicodeString(UnicodeString, &FormatU);
}
}
else
{
/* Copy the string, NULL-terminate it */
RtlCopyUnicodeString(UnicodeString, &FormatU);
}
}
static
VOID
UserpFormatMessages(
IN OUT PUNICODE_STRING TextStringU,
IN OUT PUNICODE_STRING CaptionStringU,
OUT PUINT pdwType,
IN PHARDERROR_MSG Message)
{
NTSTATUS Status;
@ -312,7 +360,7 @@ UserpFormatMessages(
PMESSAGE_RESOURCE_ENTRY MessageResource;
PWSTR FormatString, pszBuffer;
size_t cszBuffer;
ULONG Severity;
ULONG Severity = (ULONG)(Message->Status) >> 30;
ULONG Size;
/* Open client process */
@ -330,6 +378,90 @@ UserpFormatMessages(
/* Capture all string parameters from the process memory */
UserpCaptureStringParameters(Parameters, &SizeOfStrings, Message, hProcess);
/*
* Check whether it is a service notification, in which case
* we format the parameters and take the short route.
*/
if (Message->Status == STATUS_SERVICE_NOTIFICATION)
{
/* Close the process handle */
if (hProcess) NtClose(hProcess);
/*
* Retrieve the message box flags. Note that we filter out
* MB_SERVICE_NOTIFICATION to not enter an infinite recursive
* loop when we will call MessageBox() later on.
*/
*pdwType = (UINT)Parameters[2] & ~MB_SERVICE_NOTIFICATION;
/* Duplicate the UNICODE text message */
if (Message->UnicodeStringParameterMask & 0x1)
{
/* A string has been provided: duplicate it */
UserpDuplicateParamStringToUnicodeString(TextStringU, (PCWSTR)Parameters[0]);
}
else
{
/* No string (or invalid one) has been provided: keep the original buffer and reset the string length to zero */
TextStringU->Length = 0;
}
/* Duplicate the UNICODE caption */
if (Message->UnicodeStringParameterMask & 0x2)
{
/* A string has been provided: duplicate it */
UserpDuplicateParamStringToUnicodeString(CaptionStringU, (PCWSTR)Parameters[1]);
}
else
{
/* No string (or invalid one) has been provided: keep the original buffer and reset the string length to zero */
CaptionStringU->Length = 0;
}
goto Quit;
}
/* Set the message box type */
*pdwType = 0;
switch (Message->ValidResponseOptions)
{
case OptionAbortRetryIgnore:
*pdwType = MB_ABORTRETRYIGNORE;
break;
case OptionOk:
*pdwType = MB_OK;
break;
case OptionOkCancel:
*pdwType = MB_OKCANCEL;
break;
case OptionRetryCancel:
*pdwType = MB_RETRYCANCEL;
break;
case OptionYesNo:
*pdwType = MB_YESNO;
break;
case OptionYesNoCancel:
*pdwType = MB_YESNOCANCEL;
break;
case OptionShutdownSystem:
*pdwType = MB_OK;
break;
case OptionOkNoWait:
*pdwType = MB_OK;
break;
case OptionCancelTryContinue:
*pdwType = MB_CANCELTRYCONTINUE;
break;
}
/* Set the severity icon */
// STATUS_SEVERITY_SUCCESS
if (Severity == STATUS_SEVERITY_INFORMATIONAL) *pdwType |= MB_ICONINFORMATION;
else if (Severity == STATUS_SEVERITY_WARNING) *pdwType |= MB_ICONWARNING;
else if (Severity == STATUS_SEVERITY_ERROR) *pdwType |= MB_ICONERROR;
*pdwType |= MB_SYSTEMMODAL | MB_SETFOREGROUND;
/* Copy the Parameters array locally */
RtlCopyMemory(&CopyParameters, Parameters, sizeof(CopyParameters));
@ -351,8 +483,6 @@ UserpFormatMessages(
FileNameU = g_SystemProcessU;
}
Severity = (ULONG)(Message->Status) >> 30;
/* Get text string of the error code */
Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
(ULONG_PTR)RT_MESSAGETABLE,
@ -368,14 +498,25 @@ UserpFormatMessages(
}
else
{
RtlInitAnsiString(&FormatA, (PCHAR)MessageResource->Text);
RtlInitAnsiString(&FormatA, (PSTR)MessageResource->Text);
/* Status = */ RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE);
}
}
else
{
/* Fall back to hardcoded value */
RtlInitUnicodeString(&FormatU, L"Unknown Hard Error");
/*
* Fall back to hardcoded value.
* NOTE: The value used here is ReactOS-specific: it allows specifying
* the exact hard error status value and the parameters. The version
* used on Windows only says "Unknown Hard Error".
*/
#if 0
RtlInitUnicodeString(&FormatU, L"Unknown Hard Error 0x%08lx\n"
L"Parameters: 0x%p 0x%p 0x%p 0x%p");
#else
RtlInitUnicodeString(&FormatU, L"Unknown Hard Error 0x%08lx");
CopyParameters[0] = Message->Status;
#endif
FormatA.Buffer = NULL;
}
@ -444,7 +585,7 @@ UserpFormatMessages(
/* Calculate buffer length for the caption */
cszBuffer = WindowTitleU.Length + FileNameU.Length + TempStringU.Length +
3 * sizeof(WCHAR) + sizeof(UNICODE_NULL);
if (cszBuffer > CaptionStringU->MaximumLength)
if (CaptionStringU->MaximumLength < cszBuffer)
{
/* Allocate a larger buffer for the caption */
pszBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
@ -452,8 +593,8 @@ UserpFormatMessages(
cszBuffer);
if (!pszBuffer)
{
/* We could not allocate a larger buffer; continue using the smaller static buffer */
DPRINT1("Cannot allocate memory for CaptionStringU, use static buffer.\n");
/* We could not allocate a larger buffer; continue using the smaller original buffer */
DPRINT1("Cannot allocate memory for CaptionStringU, use original buffer.\n");
}
else
{
@ -496,7 +637,7 @@ UserpFormatMessages(
}
else
{
RtlInitAnsiString(&Format2A, (PCHAR)MessageResource->Text);
RtlInitAnsiString(&Format2A, (PSTR)MessageResource->Text);
/* Status = */ RtlAnsiStringToUnicodeString(&Format2U, &Format2A, TRUE);
}
@ -552,6 +693,7 @@ UserpFormatMessages(
CopyParameters[0] = (ULONG_PTR)L"unknown software exception";
}
/* Add explanation text for dialog buttons */
if (Message->ValidResponseOptions == OptionOk ||
Message->ValidResponseOptions == OptionOkCancel)
{
@ -568,7 +710,7 @@ UserpFormatMessages(
/* Calculate buffer length for the text message */
cszBuffer = FormatU.Length + SizeOfStrings + Size * sizeof(WCHAR) +
sizeof(UNICODE_NULL);
if (cszBuffer > TextStringU->MaximumLength)
if (TextStringU->MaximumLength < cszBuffer)
{
/* Allocate a larger buffer for the text message */
pszBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
@ -576,8 +718,8 @@ UserpFormatMessages(
cszBuffer);
if (!pszBuffer)
{
/* We could not allocate a larger buffer; continue using the smaller static buffer */
DPRINT1("Cannot allocate memory for TextStringU, use static buffer.\n");
/* We could not allocate a larger buffer; continue using the smaller original buffer */
DPRINT1("Cannot allocate memory for TextStringU, use original buffer.\n");
}
else
{
@ -599,6 +741,7 @@ UserpFormatMessages(
CopyParameters[0], CopyParameters[1],
CopyParameters[2], CopyParameters[3]);
/* Add explanation text for dialog buttons */
if (Message->Status == STATUS_UNHANDLED_EXCEPTION)
{
if (Message->ValidResponseOptions == OptionOk ||
@ -642,6 +785,7 @@ UserpFormatMessages(
if (Format2A.Buffer) RtlFreeUnicodeString(&Format2U);
if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
Quit:
/* Final cleanup */
UserpFreeStringParameters(Parameters, Message);
}
@ -695,9 +839,11 @@ GetRegInt(
}
static BOOL
UserpShowInformationBalloon(PWSTR Text,
PWSTR Caption,
PHARDERROR_MSG Message)
UserpShowInformationBalloon(
IN PCWSTR Text,
IN PCWSTR Caption,
IN UINT Type,
IN PHARDERROR_MSG Message)
{
ULONG ShellErrorMode;
HWND hwnd;
@ -741,7 +887,7 @@ UserpShowInformationBalloon(PWSTR Text,
if (NT_SUCCESS(Message->Status))
pdata->dwType = MB_OK;
else if (Message->Status == STATUS_SERVICE_NOTIFICATION)
pdata->dwType = Message->Parameters[2];
pdata->dwType = Type;
else
pdata->dwType = MB_ICONINFORMATION;
@ -768,68 +914,17 @@ UserpShowInformationBalloon(PWSTR Text,
}
static
ULONG
HARDERROR_RESPONSE
UserpMessageBox(
IN PCWSTR Text,
IN PCWSTR Caption,
IN ULONG ValidResponseOptions,
IN ULONG Severity,
IN ULONG Timeout)
IN UINT Type,
IN ULONG Timeout)
{
ULONG Type, MessageBoxResponse;
ULONG MessageBoxResponse;
/* Set the message box type */
switch (ValidResponseOptions)
{
case OptionAbortRetryIgnore:
Type = MB_ABORTRETRYIGNORE;
break;
case OptionOk:
Type = MB_OK;
break;
case OptionOkCancel:
Type = MB_OKCANCEL;
break;
case OptionRetryCancel:
Type = MB_RETRYCANCEL;
break;
case OptionYesNo:
Type = MB_YESNO;
break;
case OptionYesNoCancel:
Type = MB_YESNOCANCEL;
break;
case OptionShutdownSystem:
Type = MB_RETRYCANCEL; // FIXME???
break;
case OptionOkNoWait:
/*
* At that point showing the balloon failed. Is that correct?
*/
Type = MB_OK; // FIXME!
break;
case OptionCancelTryContinue:
Type = MB_CANCELTRYCONTINUE;
break;
/* Anything else is invalid */
default:
{
DPRINT1("Unknown ValidResponseOptions = %d\n", ValidResponseOptions);
return ResponseNotHandled;
}
}
/* Set severity */
// STATUS_SEVERITY_SUCCESS
if (Severity == STATUS_SEVERITY_INFORMATIONAL) Type |= MB_ICONINFORMATION;
else if (Severity == STATUS_SEVERITY_WARNING) Type |= MB_ICONWARNING;
else if (Severity == STATUS_SEVERITY_ERROR) Type |= MB_ICONERROR;
Type |= MB_SYSTEMMODAL | MB_SETFOREGROUND;
DPRINT("Text = '%S', Caption = '%S', Severity = %d, Type = 0x%lx\n",
Text, Caption, Severity, Type);
DPRINT("Text = '%S', Caption = '%S', Type = 0x%lx\n",
Text, Caption, Type);
/* Display a message box */
MessageBoxResponse = MessageBoxTimeoutW(NULL, Text, Caption, Type, 0, Timeout);
@ -846,6 +941,7 @@ UserpMessageBox(
case IDRETRY: return ResponseRetry;
case IDTRYAGAIN: return ResponseTryAgain;
case IDCONTINUE: return ResponseContinue;
default: return ResponseNotHandled;
}
return ResponseNotHandled;
@ -895,6 +991,7 @@ UserServerHardError(
IN PHARDERROR_MSG Message)
{
ULONG ErrorMode;
UINT dwType = 0;
UNICODE_STRING TextU, CaptionU;
WCHAR LocalTextBuffer[256];
WCHAR LocalCaptionBuffer[256];
@ -907,16 +1004,24 @@ UserServerHardError(
/* Make sure we don't have too many parameters */
if (Message->NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
{
// FIXME: Windows just fails (STATUS_INVALID_PARAMETER) & returns ResponseNotHandled.
// NOTE: Windows just fails (STATUS_INVALID_PARAMETER) & returns ResponseNotHandled.
DPRINT1("Invalid NumberOfParameters = %d\n", Message->NumberOfParameters);
Message->NumberOfParameters = MAXIMUM_HARDERROR_PARAMETERS;
}
if (Message->ValidResponseOptions > OptionCancelTryContinue)
{
// STATUS_INVALID_PARAMETER;
Message->Response = ResponseNotHandled;
return;
DPRINT1("Unknown ValidResponseOptions = %d\n", Message->ValidResponseOptions);
return; // STATUS_INVALID_PARAMETER;
}
if (Message->Status == STATUS_SERVICE_NOTIFICATION)
{
if (Message->NumberOfParameters < 3)
{
DPRINT1("Invalid NumberOfParameters = %d for STATUS_SERVICE_NOTIFICATION\n", Message->NumberOfParameters);
return; // STATUS_INVALID_PARAMETER;
}
// (Message->UnicodeStringParameterMask & 0x3)
}
// TODO: More message validation: check NumberOfParameters wrt. Message Status code.
/* Re-initialize the hard errors cache */
UserInitHardErrorsCache();
@ -924,7 +1029,7 @@ UserServerHardError(
/* Format the message caption and text */
RtlInitEmptyUnicodeString(&TextU, LocalTextBuffer, sizeof(LocalTextBuffer));
RtlInitEmptyUnicodeString(&CaptionU, LocalCaptionBuffer, sizeof(LocalCaptionBuffer));
UserpFormatMessages(&TextU, &CaptionU, Message);
UserpFormatMessages(&TextU, &CaptionU, &dwType, /* &Timeout, */ Message);
/* Log the hard error message */
UserpLogHardError(&TextU, &CaptionU);
@ -948,6 +1053,7 @@ UserServerHardError(
Message->Response = ResponseOk;
if (UserpShowInformationBalloon(TextU.Buffer,
CaptionU.Buffer,
dwType,
Message))
{
Message->Response = ResponseOk;
@ -958,8 +1064,7 @@ UserServerHardError(
/* Display the message box */
Message->Response = UserpMessageBox(TextU.Buffer,
CaptionU.Buffer,
Message->ValidResponseOptions,
(ULONG)(Message->Status) >> 30,
dwType,
(ULONG)-1);
Quit: