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