From 8b28efaed79c707c72ce9e2f9a7e2877fa485cf7 Mon Sep 17 00:00:00 2001 From: Andrew Nguyen Date: Sun, 18 Apr 2010 09:08:54 -0500 Subject: [PATCH] kernel32: Reject an insufficiently sized buffer in FormatMessageA/W. --- dlls/kernel32/format_msg.c | 34 +++++++++++---- dlls/kernel32/tests/format_msg.c | 94 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 9 deletions(-) diff --git a/dlls/kernel32/format_msg.c b/dlls/kernel32/format_msg.c index f579d21ffc8..fbd133b5d99 100644 --- a/dlls/kernel32/format_msg.c +++ b/dlls/kernel32/format_msg.c @@ -570,13 +570,20 @@ DWORD WINAPI FormatMessageA( *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT,max(nSize, talloced)); memcpy(*(LPSTR*)lpBuffer,target,talloced); } else { - lstrcpynA(lpBuffer,target,nSize); + if (nSize < talloced) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + goto failure; + } + strcpy(lpBuffer, target); } + + ret = talloced - 1; /* null terminator */ +failure: HeapFree(GetProcessHeap(),0,target); HeapFree(GetProcessHeap(),0,from); if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args ); - ret = (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? strlen(*(LPSTR*)lpBuffer) : strlen(lpBuffer); - TRACE("-- returning %d\n", ret); + TRACE("-- returning %u\n", ret); return ret; } #undef ADD_TO_T @@ -595,6 +602,7 @@ DWORD WINAPI FormatMessageW( __ms_va_list* args ) { struct format_args format_args; + DWORD ret = 0; LPWSTR target,t; DWORD talloced; LPWSTR from; @@ -741,21 +749,29 @@ DWORD WINAPI FormatMessageW( *t='\0'; } talloced = strlenW(target)+1; + TRACE("-- %s\n",debugstr_w(target)); if (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) { /* nSize is the MINIMUM size */ DWORD len = strlenW(target) + 1; *((LPVOID*)lpBuffer) = LocalAlloc(LMEM_ZEROINIT,len*sizeof(WCHAR)); strcpyW(*(LPWSTR*)lpBuffer, target); } - else lstrcpynW(lpBuffer, target, nSize); + else + { + if (nSize < talloced) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + goto failure; + } + strcpyW(lpBuffer, target); + } + ret = talloced - 1; /* null terminator */ +failure: HeapFree(GetProcessHeap(),0,target); HeapFree(GetProcessHeap(),0,from); if (!(dwFlags & FORMAT_MESSAGE_ARGUMENT_ARRAY)) HeapFree( GetProcessHeap(), 0, format_args.args ); - TRACE("ret=%s\n", wine_dbgstr_w((dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? - *(LPWSTR*)lpBuffer : lpBuffer)); - return (dwFlags & FORMAT_MESSAGE_ALLOCATE_BUFFER) ? - strlenW(*(LPWSTR*)lpBuffer): - strlenW(lpBuffer); + TRACE("-- returning %u\n", ret); + return ret; } #undef ADD_TO_T diff --git a/dlls/kernel32/tests/format_msg.c b/dlls/kernel32/tests/format_msg.c index fba932c59fd..f48c0f80e79 100644 --- a/dlls/kernel32/tests/format_msg.c +++ b/dlls/kernel32/tests/format_msg.c @@ -638,6 +638,98 @@ static void test_message_from_string(void) ok(r==2,"failed: r=%d\n",r); } +static void test_message_insufficient_buffer(void) +{ + static const char init_buf[] = {'x', 'x', 'x', 'x', 'x'}; + static const char expected_buf[] = {'x', 'x', 'x', 'x', 'x'}; + DWORD ret; + CHAR out[5]; + + SetLastError(0xdeadbeef); + memcpy(out, init_buf, sizeof(init_buf)); + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, "test", 0, 0, out, 0, NULL); + ok(ret == 0, "Expected FormatMessageA to return 0, got %u\n", ret); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected GetLastError() to return ERROR_INSUFFICIENT_BUFFER, got %u\n", + GetLastError()); + ok(!memcmp(expected_buf, out, sizeof(expected_buf)), + "Expected the buffer to be untouched\n"); + + SetLastError(0xdeadbeef); + memcpy(out, init_buf, sizeof(init_buf)); + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, "test", 0, 0, out, 1, NULL); + ok(ret == 0, "Expected FormatMessageA to return 0, got %u\n", ret); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected GetLastError() to return ERROR_INSUFFICIENT_BUFFER, got %u\n", + GetLastError()); + ok(!memcmp(expected_buf, out, sizeof(expected_buf)), + "Expected the buffer to be untouched\n"); + + SetLastError(0xdeadbeef); + memcpy(out, init_buf, sizeof(init_buf)); + ret = FormatMessageA(FORMAT_MESSAGE_FROM_STRING, "test", 0, 0, out, sizeof(out)/sizeof(out[0]) - 1, NULL); + ok(ret == 0, "Expected FormatMessageA to return 0, got %u\n", ret); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected GetLastError() to return ERROR_INSUFFICIENT_BUFFER, got %u\n", + GetLastError()); + ok(!memcmp(expected_buf, out, sizeof(expected_buf)), + "Expected the buffer to be untouched\n"); +} + +static void test_message_insufficient_buffer_wide(void) +{ + static const WCHAR test[] = {'t','e','s','t',0}; + static const WCHAR init_buf[] = {'x', 'x', 'x', 'x', 'x'}; + static const WCHAR expected_buf[] = {'x', 'x', 'x', 'x', 'x'}; + static const WCHAR broken_buf[] = {0, 'x', 'x', 'x', 'x'}; + static const WCHAR broken2_buf[] = {'t','e','s',0,'x'}; + + DWORD ret; + WCHAR out[5]; + + SetLastError(0xdeadbeef); + ret = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, NULL, 0, 0, NULL, 0, NULL); + if (!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + win_skip("FormatMessageW is not implemented\n"); + return; + } + + SetLastError(0xdeadbeef); + memcpy(out, init_buf, sizeof(init_buf)); + ret = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, test, 0, 0, out, 0, NULL); + ok(ret == 0, "Expected FormatMessageA to return 0, got %u\n", ret); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected GetLastError() to return ERROR_INSUFFICIENT_BUFFER, got %u\n", + GetLastError()); + ok(!memcmp(expected_buf, out, sizeof(expected_buf)), + "Expected the buffer to be untouched\n"); + + /* Windows Server 2003 and newer report failure but copy a + * truncated string to the buffer for non-zero buffer sizes. */ + SetLastError(0xdeadbeef); + memcpy(out, init_buf, sizeof(init_buf)); + ret = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, test, 0, 0, out, 1, NULL); + ok(ret == 0, "Expected FormatMessageA to return 0, got %u\n", ret); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected GetLastError() to return ERROR_INSUFFICIENT_BUFFER, got %u\n", + GetLastError()); + ok(!memcmp(expected_buf, out, sizeof(expected_buf)) || + broken(!memcmp(broken_buf, out, sizeof(broken_buf))), /* W2K3+ */ + "Expected the buffer to be untouched\n"); + + SetLastError(0xdeadbeef); + memcpy(out, init_buf, sizeof(init_buf)); + ret = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, test, 0, 0, out, sizeof(out)/sizeof(out[0]) - 1, NULL); + ok(ret == 0, "Expected FormatMessageA to return 0, got %u\n", ret); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, + "Expected GetLastError() to return ERROR_INSUFFICIENT_BUFFER, got %u\n", + GetLastError()); + ok(!memcmp(expected_buf, out, sizeof(expected_buf)) || + broken(!memcmp(broken2_buf, out, sizeof(broken2_buf))), /* W2K3+ */ + "Expected the buffer to be untouched\n"); +} + static void test_message_null_buffer(void) { DWORD ret, error; @@ -727,6 +819,8 @@ START_TEST(format_msg) { test_message_from_string(); test_message_from_string_wide(); + test_message_insufficient_buffer(); + test_message_insufficient_buffer_wide(); test_message_null_buffer(); test_message_from_hmodule(); } -- 2.11.4.GIT