gh-106844: Fix issues in _winapi.LCMapStringEx (GH-107832)

* Strings with length from 2**31-1 to 2**32-2 always caused MemoryError,
   it doesn't matter how much memory is available.
* Strings with length exactly 2**32-1 caused OSError.
* Strings longer than 2**32-1 characters were truncated due to integer overflow bug.
* Strings containing the null character were truncated at the first null character.

Now strings longer than 2**31-1 characters caused OverflowError and the null character is allowed.
This commit is contained in:
Serhiy Storchaka 2023-08-11 21:13:46 +03:00 committed by GitHub
parent a39f0a3506
commit 04cc01453d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 32 additions and 16 deletions

View file

@ -1036,6 +1036,7 @@ class PathLikeTests(NtpathTestCase):
self._check_function(self.path.normcase) self._check_function(self.path.normcase)
if sys.platform == 'win32': if sys.platform == 'win32':
self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ') self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ')
self.assertEqual(ntpath.normcase('abc\x00def'), 'abc\x00def')
def test_path_isabs(self): def test_path_isabs(self):
self._check_function(self.path.isabs) self._check_function(self.path.isabs)

View file

@ -0,0 +1 @@
Fix integer overflow and truncating by the null character in :func:`!_winapi.LCMapStringEx` which affects :func:`ntpath.normcase`.

View file

@ -1539,40 +1539,56 @@ _winapi.LCMapStringEx
locale: LPCWSTR locale: LPCWSTR
flags: DWORD flags: DWORD
src: LPCWSTR src: unicode
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static PyObject *
_winapi_LCMapStringEx_impl(PyObject *module, LPCWSTR locale, DWORD flags, _winapi_LCMapStringEx_impl(PyObject *module, LPCWSTR locale, DWORD flags,
LPCWSTR src) PyObject *src)
/*[clinic end generated code: output=cf4713d80e2b47c9 input=9fe26f95d5ab0001]*/ /*[clinic end generated code: output=b90e6b26e028ff0a input=3e3dcd9b8164012f]*/
{ {
if (flags & (LCMAP_SORTHANDLE | LCMAP_HASH | LCMAP_BYTEREV | if (flags & (LCMAP_SORTHANDLE | LCMAP_HASH | LCMAP_BYTEREV |
LCMAP_SORTKEY)) { LCMAP_SORTKEY)) {
return PyErr_Format(PyExc_ValueError, "unsupported flags"); return PyErr_Format(PyExc_ValueError, "unsupported flags");
} }
int dest_size = LCMapStringEx(locale, flags, src, -1, NULL, 0, Py_ssize_t src_size;
wchar_t *src_ = PyUnicode_AsWideCharString(src, &src_size);
if (!src_) {
return NULL;
}
if (src_size > INT_MAX) {
PyMem_Free(src_);
PyErr_SetString(PyExc_OverflowError, "input string is too long");
return NULL;
}
int dest_size = LCMapStringEx(locale, flags, src_, (int)src_size, NULL, 0,
NULL, NULL, 0); NULL, NULL, 0);
if (dest_size == 0) { if (dest_size <= 0) {
return PyErr_SetFromWindowsErr(0); DWORD error = GetLastError();
PyMem_Free(src_);
return PyErr_SetFromWindowsErr(error);
} }
wchar_t* dest = PyMem_NEW(wchar_t, dest_size); wchar_t* dest = PyMem_NEW(wchar_t, dest_size);
if (dest == NULL) { if (dest == NULL) {
PyMem_Free(src_);
return PyErr_NoMemory(); return PyErr_NoMemory();
} }
int nmapped = LCMapStringEx(locale, flags, src, -1, dest, dest_size, int nmapped = LCMapStringEx(locale, flags, src_, (int)src_size, dest, dest_size,
NULL, NULL, 0); NULL, NULL, 0);
if (nmapped == 0) { if (nmapped <= 0) {
DWORD error = GetLastError(); DWORD error = GetLastError();
PyMem_Free(src_);
PyMem_DEL(dest); PyMem_DEL(dest);
return PyErr_SetFromWindowsErr(error); return PyErr_SetFromWindowsErr(error);
} }
PyObject *ret = PyUnicode_FromWideChar(dest, dest_size - 1); PyMem_Free(src_);
PyObject *ret = PyUnicode_FromWideChar(dest, nmapped);
PyMem_DEL(dest); PyMem_DEL(dest);
return ret; return ret;

View file

@ -884,7 +884,7 @@ PyDoc_STRVAR(_winapi_LCMapStringEx__doc__,
static PyObject * static PyObject *
_winapi_LCMapStringEx_impl(PyObject *module, LPCWSTR locale, DWORD flags, _winapi_LCMapStringEx_impl(PyObject *module, LPCWSTR locale, DWORD flags,
LPCWSTR src); PyObject *src);
static PyObject * static PyObject *
_winapi_LCMapStringEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) _winapi_LCMapStringEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
@ -911,16 +911,16 @@ _winapi_LCMapStringEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs,
static const char * const _keywords[] = {"locale", "flags", "src", NULL}; static const char * const _keywords[] = {"locale", "flags", "src", NULL};
static _PyArg_Parser _parser = { static _PyArg_Parser _parser = {
.keywords = _keywords, .keywords = _keywords,
.format = "O&kO&:LCMapStringEx", .format = "O&kU:LCMapStringEx",
.kwtuple = KWTUPLE, .kwtuple = KWTUPLE,
}; };
#undef KWTUPLE #undef KWTUPLE
LPCWSTR locale = NULL; LPCWSTR locale = NULL;
DWORD flags; DWORD flags;
LPCWSTR src = NULL; PyObject *src;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
_PyUnicode_WideCharString_Converter, &locale, &flags, _PyUnicode_WideCharString_Converter, &src)) { _PyUnicode_WideCharString_Converter, &locale, &flags, &src)) {
goto exit; goto exit;
} }
return_value = _winapi_LCMapStringEx_impl(module, locale, flags, src); return_value = _winapi_LCMapStringEx_impl(module, locale, flags, src);
@ -928,8 +928,6 @@ _winapi_LCMapStringEx(PyObject *module, PyObject *const *args, Py_ssize_t nargs,
exit: exit:
/* Cleanup for locale */ /* Cleanup for locale */
PyMem_Free((void *)locale); PyMem_Free((void *)locale);
/* Cleanup for src */
PyMem_Free((void *)src);
return return_value; return return_value;
} }
@ -1480,4 +1478,4 @@ exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=f32fe6ecdbffd74d input=a9049054013a1b77]*/ /*[clinic end generated code: output=ff91ab5cae8961dd input=a9049054013a1b77]*/