Andrew Nguyen : kernel32: Reject an insufficiently sized buffer in FormatMessageA/W.
Alexandre Julliard
julliard at winehq.org
Mon Apr 19 11:51:19 CDT 2010
Module: wine
Branch: master
Commit: 8b28efaed79c707c72ce9e2f9a7e2877fa485cf7
URL: http://source.winehq.org/git/wine.git/?a=commit;h=8b28efaed79c707c72ce9e2f9a7e2877fa485cf7
Author: Andrew Nguyen <anguyen at codeweavers.com>
Date: Sun Apr 18 09:08:54 2010 -0500
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 f579d21..fbd133b 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 fba932c..f48c0f8 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();
}
More information about the wine-cvs
mailing list