Skip to content

Commit b5176a8

Browse files
[3.12] gh-106844: Fix issues in _winapi.LCMapStringEx (GH-107832) (#107874)
* 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.. (cherry picked from commit 04cc014)
1 parent aa9707d commit b5176a8

File tree

4 files changed

+32
-16
lines changed

4 files changed

+32
-16
lines changed

Lib/test/test_ntpath.py

+1
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,7 @@ def test_path_normcase(self):
10361036
self._check_function(self.path.normcase)
10371037
if sys.platform == 'win32':
10381038
self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ')
1039+
self.assertEqual(ntpath.normcase('abc\x00def'), 'abc\x00def')
10391040

10401041
def test_path_isabs(self):
10411042
self._check_function(self.path.isabs)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix integer overflow and truncating by the null character in :func:`!_winapi.LCMapStringEx` which affects :func:`ntpath.normcase`.

Modules/_winapi.c

+25-9
Original file line numberDiff line numberDiff line change
@@ -1538,40 +1538,56 @@ _winapi.LCMapStringEx
15381538
15391539
locale: LPCWSTR
15401540
flags: DWORD
1541-
src: LPCWSTR
1541+
src: unicode
15421542
15431543
[clinic start generated code]*/
15441544

15451545
static PyObject *
15461546
_winapi_LCMapStringEx_impl(PyObject *module, LPCWSTR locale, DWORD flags,
1547-
LPCWSTR src)
1548-
/*[clinic end generated code: output=cf4713d80e2b47c9 input=9fe26f95d5ab0001]*/
1547+
PyObject *src)
1548+
/*[clinic end generated code: output=b90e6b26e028ff0a input=3e3dcd9b8164012f]*/
15491549
{
15501550
if (flags & (LCMAP_SORTHANDLE | LCMAP_HASH | LCMAP_BYTEREV |
15511551
LCMAP_SORTKEY)) {
15521552
return PyErr_Format(PyExc_ValueError, "unsupported flags");
15531553
}
15541554

1555-
int dest_size = LCMapStringEx(locale, flags, src, -1, NULL, 0,
1555+
Py_ssize_t src_size;
1556+
wchar_t *src_ = PyUnicode_AsWideCharString(src, &src_size);
1557+
if (!src_) {
1558+
return NULL;
1559+
}
1560+
if (src_size > INT_MAX) {
1561+
PyMem_Free(src_);
1562+
PyErr_SetString(PyExc_OverflowError, "input string is too long");
1563+
return NULL;
1564+
}
1565+
1566+
int dest_size = LCMapStringEx(locale, flags, src_, (int)src_size, NULL, 0,
15561567
NULL, NULL, 0);
1557-
if (dest_size == 0) {
1558-
return PyErr_SetFromWindowsErr(0);
1568+
if (dest_size <= 0) {
1569+
DWORD error = GetLastError();
1570+
PyMem_Free(src_);
1571+
return PyErr_SetFromWindowsErr(error);
15591572
}
15601573

15611574
wchar_t* dest = PyMem_NEW(wchar_t, dest_size);
15621575
if (dest == NULL) {
1576+
PyMem_Free(src_);
15631577
return PyErr_NoMemory();
15641578
}
15651579

1566-
int nmapped = LCMapStringEx(locale, flags, src, -1, dest, dest_size,
1580+
int nmapped = LCMapStringEx(locale, flags, src_, (int)src_size, dest, dest_size,
15671581
NULL, NULL, 0);
1568-
if (nmapped == 0) {
1582+
if (nmapped <= 0) {
15691583
DWORD error = GetLastError();
1584+
PyMem_Free(src_);
15701585
PyMem_DEL(dest);
15711586
return PyErr_SetFromWindowsErr(error);
15721587
}
15731588

1574-
PyObject *ret = PyUnicode_FromWideChar(dest, dest_size - 1);
1589+
PyMem_Free(src_);
1590+
PyObject *ret = PyUnicode_FromWideChar(dest, nmapped);
15751591
PyMem_DEL(dest);
15761592

15771593
return ret;

Modules/clinic/_winapi.c.h

+5-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)