Cause of bug discovered by Jason Edmeades.
Some applications partially overlap the two buffers, so don't assume they don't overlap.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=38558 Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
The code uses the same method as wcstombs now.
libs/port/mbtowc.c | 65 +++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 24 deletions(-)
diff --git a/libs/port/mbtowc.c b/libs/port/mbtowc.c index 4977c82..0994ade 100644 --- a/libs/port/mbtowc.c +++ b/libs/port/mbtowc.c @@ -65,34 +65,51 @@ static inline int mbstowcs_sbcs( const struct sbcs_table *table, int flags, ret = -1; }
- for (;;) + while (srclen >= 16) { - switch(srclen) - { - default: - case 16: dst[15] = cp2uni[src[15]]; - case 15: dst[14] = cp2uni[src[14]]; - case 14: dst[13] = cp2uni[src[13]]; - case 13: dst[12] = cp2uni[src[12]]; - case 12: dst[11] = cp2uni[src[11]]; - case 11: dst[10] = cp2uni[src[10]]; - case 10: dst[9] = cp2uni[src[9]]; - case 9: dst[8] = cp2uni[src[8]]; - case 8: dst[7] = cp2uni[src[7]]; - case 7: dst[6] = cp2uni[src[6]]; - case 6: dst[5] = cp2uni[src[5]]; - case 5: dst[4] = cp2uni[src[4]]; - case 4: dst[3] = cp2uni[src[3]]; - case 3: dst[2] = cp2uni[src[2]]; - case 2: dst[1] = cp2uni[src[1]]; - case 1: dst[0] = cp2uni[src[0]]; - case 0: break; - } - if (srclen < 16) return ret; - dst += 16; + dst[0] = cp2uni[src[0]]; + dst[1] = cp2uni[src[1]]; + dst[2] = cp2uni[src[2]]; + dst[3] = cp2uni[src[3]]; + dst[4] = cp2uni[src[4]]; + dst[5] = cp2uni[src[5]]; + dst[6] = cp2uni[src[6]]; + dst[7] = cp2uni[src[7]]; + dst[8] = cp2uni[src[8]]; + dst[9] = cp2uni[src[9]]; + dst[10] = cp2uni[src[10]]; + dst[11] = cp2uni[src[11]]; + dst[12] = cp2uni[src[12]]; + dst[13] = cp2uni[src[13]]; + dst[14] = cp2uni[src[14]]; + dst[15] = cp2uni[src[15]]; src += 16; + dst += 16; srclen -= 16; } + + /* now handle the remaining characters */ + src += srclen; + dst += srclen; + switch (srclen) + { + case 15: dst[-15] = cp2uni[src[-15]]; + case 14: dst[-14] = cp2uni[src[-14]]; + case 13: dst[-13] = cp2uni[src[-13]]; + case 12: dst[-12] = cp2uni[src[-12]]; + case 11: dst[-11] = cp2uni[src[-11]]; + case 10: dst[-10] = cp2uni[src[-10]]; + case 9: dst[-9] = cp2uni[src[-9]]; + case 8: dst[-8] = cp2uni[src[-8]]; + case 7: dst[-7] = cp2uni[src[-7]]; + case 6: dst[-6] = cp2uni[src[-6]]; + case 5: dst[-5] = cp2uni[src[-5]]; + case 4: dst[-4] = cp2uni[src[-4]]; + case 3: dst[-3] = cp2uni[src[-3]]; + case 2: dst[-2] = cp2uni[src[-2]]; + case 1: dst[-1] = cp2uni[src[-1]]; + } + return ret; }
/* mbstowcs for single-byte code page with char decomposition */
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/kernel32/tests/codepage.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/dlls/kernel32/tests/codepage.c b/dlls/kernel32/tests/codepage.c index b15a3a1..38f094f 100644 --- a/dlls/kernel32/tests/codepage.c +++ b/dlls/kernel32/tests/codepage.c @@ -314,12 +314,22 @@ static void test_overlapped_buffers(void) char buf[256]; int ret;
+ /* limit such that strA's NUL terminator overlaps strW's NUL */ + size_t overlap_limit = (sizeof(strW)-sizeof(strA)) - (sizeof(strW[0])-sizeof(strA[0])); + SetLastError(0xdeadbeef); memcpy(buf + 1, strW, sizeof(strW)); ret = WideCharToMultiByte(CP_ACP, 0, (WCHAR *)(buf + 1), -1, buf, sizeof(buf), NULL, NULL); ok(ret == sizeof(strA), "unexpected ret %d\n", ret); ok(!memcmp(buf, strA, sizeof(strA)), "conversion failed: %s\n", buf); ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError()); + + SetLastError(0xdeadbeef); + memcpy(buf + overlap_limit, strA, sizeof(strA)); + ret = MultiByteToWideChar(CP_ACP, 0, buf + overlap_limit, -1, (WCHAR *)buf, sizeof(buf) / sizeof(WCHAR)); + ok(ret == ARRAY_SIZE(strW), "unexpected ret %d\n", ret); + ok(!memcmp(buf, strW, sizeof(strW)), "conversion failed: %s\n", wine_dbgstr_wn((WCHAR *)buf, ARRAY_SIZE(strW))); + ok(GetLastError() == 0xdeadbeef, "GetLastError() is %u\n", GetLastError()); }
static void test_string_conversion(LPBOOL bUsedDefaultChar)