Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Lib/test/test_ntpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,7 @@ def test_path_normcase(self):
self._check_function(self.path.normcase)
if sys.platform == 'win32':
self.assertEqual(ntpath.normcase('\u03a9\u2126'), 'ωΩ')
self.assertEqual(ntpath.normcase('abc\x00def'), 'abc\x00def')

def test_path_isabs(self):
self._check_function(self.path.isabs)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix null-bytes handling in ``LCMapStringEx`` which affects
:func:`ntpath.normcase`.
40 changes: 32 additions & 8 deletions Modules/_winapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -1537,42 +1537,66 @@ _winapi_PeekNamedPipe_impl(PyObject *module, HANDLE handle, int size)
/*[clinic input]
_winapi.LCMapStringEx

locale: LPCWSTR
locale: unicode

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change it for locale?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just backported the solution from 927b5af#diff-42415407f8d0ef2d42e29d13d979f633e3543770e62c3871e1101ad532d336a8R1518

So, 3.11 and 3.12 would match.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code would be simpler if keep the LPCWSTR converter here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. We don't need to treat this argument any differently - it's only the src argument that requires special handling.

flags: DWORD
src: LPCWSTR
src: unicode

[clinic start generated code]*/

static PyObject *
_winapi_LCMapStringEx_impl(PyObject *module, LPCWSTR locale, DWORD flags,
LPCWSTR src)
/*[clinic end generated code: output=cf4713d80e2b47c9 input=9fe26f95d5ab0001]*/
_winapi_LCMapStringEx_impl(PyObject *module, PyObject *locale, DWORD flags,
PyObject *src)
/*[clinic end generated code: output=8ea4c9d85a4a1f23 input=2fa6ebc92591731b]*/
{
if (flags & (LCMAP_SORTHANDLE | LCMAP_HASH | LCMAP_BYTEREV |
LCMAP_SORTKEY)) {
return PyErr_Format(PyExc_ValueError, "unsupported flags");
}

int dest_size = LCMapStringEx(locale, flags, src, -1, NULL, 0,
wchar_t *locale_ = PyUnicode_AsWideCharString(locale, NULL);
if (!locale_) {
return NULL;
}
Py_ssize_t srcLenAsSsize;
int srcLen;
wchar_t *src_ = PyUnicode_AsWideCharString(src, &srcLenAsSsize);
if (!src_) {
PyMem_Free(locale_);
return NULL;
}
srcLen = (int)srcLenAsSsize;
if (srcLen != srcLenAsSsize) {
srcLen = -1;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would it work for large (more than 2**32 characters) string containing NUL?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't, but that's the caller's responsibility.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LCMapStringEx() will simply return the result for the truncated input. It is bad. How is the caller expected to detect this problem if it only occurs in special circumferences?

What does LCMapStringEx() return for large input not containing NUL? Most likely it fails. We should either emulate this response (raise an OSError with the same Windows error code) or simply raise an OverflowError.


int dest_size = LCMapStringEx(locale_, flags, src_, srcLen, NULL, 0,
NULL, NULL, 0);
if (dest_size == 0) {
PyMem_Free(locale_);
PyMem_Free(src_);
return PyErr_SetFromWindowsErr(0);
}

wchar_t* dest = PyMem_NEW(wchar_t, dest_size);
if (dest == NULL) {
PyMem_Free(locale_);
PyMem_Free(src_);
return PyErr_NoMemory();
}

int nmapped = LCMapStringEx(locale, flags, src, -1, dest, dest_size,
int nmapped = LCMapStringEx(locale_, flags, src_, srcLen, dest, dest_size,
NULL, NULL, 0);
if (nmapped == 0) {
DWORD error = GetLastError();
PyMem_Free(locale_);
PyMem_Free(src_);
PyMem_DEL(dest);
return PyErr_SetFromWindowsErr(error);
}

PyObject *ret = PyUnicode_FromWideChar(dest, dest_size - 1);
PyObject *ret = PyUnicode_FromWideChar(dest, dest_size);
PyMem_Free(locale_);
PyMem_Free(src_);
PyMem_DEL(dest);

return ret;
Expand Down
19 changes: 7 additions & 12 deletions Modules/clinic/_winapi.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.