Cygwin: fix buffer overrun in cygwin_strcasecmp

sys_mbstowcs is called with the destination buffer length
set to MaximumLength from the receiving UNICODE_STRING buffer.
This is twice as much as the actual size of the buffer in
wchar_t units, which is the unit expected by sys_mbstowcs.

sys_mbstowcs always attaches a NUL, within the destination
buffersize given.  But if the string is exactly one wchar_t
less than the actual buffer, and the buffersize is given too
large, sys_mbstowcs writes a NUL one wchar_t beyond the buffer.

This has only been exposed with Cygwin 3.1.5 because alloca
on newer gcc 9 apparently allocates more tightly.  The alloca
buffer here is requested with 16 bytes, which is exactly the
number of bytes required for the string L"cmd.exe".  Older gcc
apparently allocated a few more bytes on the stack, while gcc 9
allocates in 16 byte granularity...

Fix this by giving the correct destination buffer size to
sys_mbstowcs.

Fixes: https://cygwin.com/pipermail/cygwin/2020-June/245226.html
Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2020-07-06 13:17:53 +02:00
parent f095752167
commit bb96bd03b0
2 changed files with 22 additions and 13 deletions

View File

@ -9,3 +9,6 @@ Bug Fixes:
---------- ----------
- Fix IPPROTO_TCP option handling, especially in terms of TCP_MAXSEG. - Fix IPPROTO_TCP option handling, especially in terms of TCP_MAXSEG.
- Fix a buffer overrun in Cygwin-internal string comparison.
Fixes: https://cygwin.com/pipermail/cygwin/2020-June/245226.html

View File

@ -635,7 +635,7 @@ sys_cp_mbstowcs (mbtowc_p f_mbtowc, wchar_t *dst, size_t dlen,
/* The technique is based on a discussion here: /* The technique is based on a discussion here:
http://www.mail-archive.com/linux-utf8@nl.linux.org/msg00080.html http://www.mail-archive.com/linux-utf8@nl.linux.org/msg00080.html
Invalid bytes in a multibyte secuence are converted to Invalid bytes in a multibyte sequence are converted to
the private use area which is already used to store ASCII the private use area which is already used to store ASCII
chars invalid in Windows filenames. This technque allows chars invalid in Windows filenames. This technque allows
to store them in a symmetric way. */ to store them in a symmetric way. */
@ -801,14 +801,18 @@ extern "C" int __stdcall
cygwin_strcasecmp (const char *cs, const char *ct) cygwin_strcasecmp (const char *cs, const char *ct)
{ {
UNICODE_STRING us, ut; UNICODE_STRING us, ut;
ULONG len; ULONG len, ulen;
len = strlen (cs) + 1;
ulen = len * sizeof (WCHAR);
RtlInitEmptyUnicodeString (&us, (PWCHAR) alloca (ulen), ulen);
us.Length = sys_mbstowcs (us.Buffer, len, cs) * sizeof (WCHAR);
len = strlen (ct) + 1;
ulen = len * sizeof (WCHAR);
RtlInitEmptyUnicodeString (&ut, (PWCHAR) alloca (ulen), ulen);
ut.Length = sys_mbstowcs (ut.Buffer, len, ct) * sizeof (WCHAR);
len = (strlen (cs) + 1) * sizeof (WCHAR);
RtlInitEmptyUnicodeString (&us, (PWCHAR) alloca (len), len);
us.Length = sys_mbstowcs (us.Buffer, us.MaximumLength, cs) * sizeof (WCHAR);
len = (strlen (ct) + 1) * sizeof (WCHAR);
RtlInitEmptyUnicodeString (&ut, (PWCHAR) alloca (len), len);
ut.Length = sys_mbstowcs (ut.Buffer, ut.MaximumLength, ct) * sizeof (WCHAR);
return RtlCompareUnicodeString (&us, &ut, TRUE); return RtlCompareUnicodeString (&us, &ut, TRUE);
} }
@ -816,19 +820,21 @@ extern "C" int __stdcall
cygwin_strncasecmp (const char *cs, const char *ct, size_t n) cygwin_strncasecmp (const char *cs, const char *ct, size_t n)
{ {
UNICODE_STRING us, ut; UNICODE_STRING us, ut;
ULONG len; ULONG ulen;
size_t ls = 0, lt = 0; size_t ls = 0, lt = 0;
while (cs[ls] && ls < n) while (cs[ls] && ls < n)
++ls; ++ls;
len = (ls + 1) * sizeof (WCHAR); ulen = (ls + 1) * sizeof (WCHAR);
RtlInitEmptyUnicodeString (&us, (PWCHAR) alloca (len), len); RtlInitEmptyUnicodeString (&us, (PWCHAR) alloca (ulen), ulen);
us.Length = sys_mbstowcs (us.Buffer, ls + 1, cs, ls) * sizeof (WCHAR); us.Length = sys_mbstowcs (us.Buffer, ls + 1, cs, ls) * sizeof (WCHAR);
while (ct[lt] && lt < n) while (ct[lt] && lt < n)
++lt; ++lt;
len = (lt + 1) * sizeof (WCHAR); ulen = (lt + 1) * sizeof (WCHAR);
RtlInitEmptyUnicodeString (&ut, (PWCHAR) alloca (len), len); RtlInitEmptyUnicodeString (&ut, (PWCHAR) alloca (ulen), ulen);
ut.Length = sys_mbstowcs (ut.Buffer, lt + 1, ct, lt) * sizeof (WCHAR); ut.Length = sys_mbstowcs (ut.Buffer, lt + 1, ct, lt) * sizeof (WCHAR);
return RtlCompareUnicodeString (&us, &ut, TRUE); return RtlCompareUnicodeString (&us, &ut, TRUE);
} }