From: Daniel Lehman dlehman25@gmail.com
va_list passed to RtlFormatMessage is modified even on error in this case, if the buffer is not large enough, STATUS_BUFFER_OVERFLOW is returned and FormatMessage tries again, but the va_list pointer is now moved to a later argument, so the next call reads off the end, crashing
Signed-off-by: Daniel Lehman dlehman25@gmail.com --- dlls/kernelbase/locale.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-)
diff --git a/dlls/kernelbase/locale.c b/dlls/kernelbase/locale.c index ac4dc188345..d4887722209 100644 --- a/dlls/kernelbase/locale.c +++ b/dlls/kernelbase/locale.c @@ -5352,6 +5352,7 @@ DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageW( DWORD flags, const void *source, if (flags & FORMAT_MESSAGE_ALLOCATE_BUFFER) { WCHAR *result; + va_list args_copy; ULONG alloc = max( size * sizeof(WCHAR), 65536 );
for (;;) @@ -5361,9 +5362,17 @@ DWORD WINAPI DECLSPEC_HOTPATCH FormatMessageW( DWORD flags, const void *source, status = STATUS_NO_MEMORY; break; } - status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS), - FALSE, !!(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY), args, - result, alloc, &retsize ); + if (args && !(flags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) + { + va_copy( args_copy, *args ); + status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS), + FALSE, FALSE, &args_copy, result, alloc, &retsize ); + va_end( args_copy ); + } + else + status = RtlFormatMessage( src, width, !!(flags & FORMAT_MESSAGE_IGNORE_INSERTS), + FALSE, TRUE, args, result, alloc, &retsize ); + if (!status) { if (retsize <= sizeof(WCHAR)) HeapFree( GetProcessHeap(), 0, result );