Skip to content

Commit b4d4aa9

Browse files
gh-81489: Use Unicode APIs for mmap tagname on Windows (GH-14133)
Co-authored-by: Erlend E. Aasland <[email protected]>
1 parent 2f126a7 commit b4d4aa9

File tree

5 files changed

+23
-25
lines changed

5 files changed

+23
-25
lines changed

Doc/library/mmap.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ To map anonymous memory, -1 should be passed as the fileno along with the length
6262
the same file. If you specify the name of an existing tag, that tag is
6363
opened, otherwise a new tag of this name is created. If this parameter is
6464
omitted or ``None``, the mapping is created without a name. Avoiding the
65-
use of the tag parameter will assist in keeping your code portable between
66-
Unix and Windows.
65+
use of the *tagname* parameter will assist in keeping your code portable
66+
between Unix and Windows.
6767

6868
*offset* may be specified as a non-negative integer offset. mmap references
6969
will be relative to the offset from the beginning of the file. *offset*

Lib/test/test_mmap.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -672,14 +672,16 @@ def test_tagname(self):
672672
m2.close()
673673
m1.close()
674674

675+
with self.assertRaisesRegex(TypeError, 'tagname'):
676+
mmap.mmap(-1, 8, tagname=1)
677+
675678
@cpython_only
676679
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
677680
def test_sizeof(self):
678681
m1 = mmap.mmap(-1, 100)
679682
tagname = random_tagname()
680683
m2 = mmap.mmap(-1, 100, tagname=tagname)
681-
self.assertEqual(sys.getsizeof(m2),
682-
sys.getsizeof(m1) + len(tagname) + 1)
684+
self.assertGreater(sys.getsizeof(m2), sys.getsizeof(m1))
683685

684686
@unittest.skipUnless(os.name == 'nt', 'requires Windows')
685687
def test_crasher_on_windows(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix mojibake in :class:`mmap.mmap` when using a non-ASCII *tagname* argument
2+
on Windows.

Modules/mmapmodule.c

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@
3232
# include <unistd.h> // close()
3333
#endif
3434

35-
// to support MS_WINDOWS_SYSTEM OpenFileMappingA / CreateFileMappingA
36-
// need to be replaced with OpenFileMappingW / CreateFileMappingW
37-
#if !defined(MS_WINDOWS) || defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_GAMES)
38-
3935
#ifndef MS_WINDOWS
4036
#define UNIX
4137
# ifdef HAVE_FCNTL_H
@@ -116,7 +112,7 @@ typedef struct {
116112
#ifdef MS_WINDOWS
117113
HANDLE map_handle;
118114
HANDLE file_handle;
119-
char * tagname;
115+
wchar_t * tagname;
120116
#endif
121117

122118
#ifdef UNIX
@@ -534,7 +530,7 @@ mmap_resize_method(mmap_object *self,
534530
CloseHandle(self->map_handle);
535531
/* if the file mapping still exists, it cannot be resized. */
536532
if (self->tagname) {
537-
self->map_handle = OpenFileMappingA(FILE_MAP_WRITE, FALSE,
533+
self->map_handle = OpenFileMappingW(FILE_MAP_WRITE, FALSE,
538534
self->tagname);
539535
if (self->map_handle) {
540536
PyErr_SetFromWindowsErr(ERROR_USER_MAPPED_FILE);
@@ -563,7 +559,7 @@ mmap_resize_method(mmap_object *self,
563559

564560
/* create a new file mapping and map a new view */
565561
/* FIXME: call CreateFileMappingW with wchar_t tagname */
566-
self->map_handle = CreateFileMappingA(
562+
self->map_handle = CreateFileMappingW(
567563
self->file_handle,
568564
NULL,
569565
PAGE_READWRITE,
@@ -845,7 +841,7 @@ mmap__sizeof__method(mmap_object *self, void *Py_UNUSED(ignored))
845841
{
846842
size_t res = _PyObject_SIZE(Py_TYPE(self));
847843
if (self->tagname) {
848-
res += strlen(self->tagname) + 1;
844+
res += (wcslen(self->tagname) + 1) * sizeof(self->tagname[0]);
849845
}
850846
return PyLong_FromSize_t(res);
851847
}
@@ -1400,7 +1396,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
14001396
DWORD off_lo; /* lower 32 bits of offset */
14011397
DWORD size_hi; /* upper 32 bits of size */
14021398
DWORD size_lo; /* lower 32 bits of size */
1403-
const char *tagname = "";
1399+
PyObject *tagname = Py_None;
14041400
DWORD dwErr = 0;
14051401
int fileno;
14061402
HANDLE fh = 0;
@@ -1410,7 +1406,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
14101406
"tagname",
14111407
"access", "offset", NULL };
14121408

1413-
if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|ziL", keywords,
1409+
if (!PyArg_ParseTupleAndKeywords(args, kwdict, "in|OiL", keywords,
14141410
&fileno, &map_size,
14151411
&tagname, &access, &offset)) {
14161412
return NULL;
@@ -1543,17 +1539,19 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
15431539
m_obj->weakreflist = NULL;
15441540
m_obj->exports = 0;
15451541
/* set the tag name */
1546-
if (tagname != NULL && *tagname != '\0') {
1547-
m_obj->tagname = PyMem_Malloc(strlen(tagname)+1);
1542+
if (!Py_IsNone(tagname)) {
1543+
if (!PyUnicode_Check(tagname)) {
1544+
Py_DECREF(m_obj);
1545+
return PyErr_Format(PyExc_TypeError, "expected str or None for "
1546+
"'tagname', not %.200s",
1547+
Py_TYPE(tagname)->tp_name);
1548+
}
1549+
m_obj->tagname = PyUnicode_AsWideCharString(tagname, NULL);
15481550
if (m_obj->tagname == NULL) {
1549-
PyErr_NoMemory();
15501551
Py_DECREF(m_obj);
15511552
return NULL;
15521553
}
1553-
strcpy(m_obj->tagname, tagname);
15541554
}
1555-
else
1556-
m_obj->tagname = NULL;
15571555

15581556
m_obj->access = (access_mode)access;
15591557
size_hi = (DWORD)(size >> 32);
@@ -1562,7 +1560,7 @@ new_mmap_object(PyTypeObject *type, PyObject *args, PyObject *kwdict)
15621560
off_lo = (DWORD)(offset & 0xFFFFFFFF);
15631561
/* For files, it would be sufficient to pass 0 as size.
15641562
For anonymous maps, we have to pass the size explicitly. */
1565-
m_obj->map_handle = CreateFileMappingA(m_obj->file_handle,
1563+
m_obj->map_handle = CreateFileMappingW(m_obj->file_handle,
15661564
NULL,
15671565
flProtect,
15681566
size_hi,
@@ -1771,5 +1769,3 @@ PyInit_mmap(void)
17711769
{
17721770
return PyModuleDef_Init(&mmapmodule);
17731771
}
1774-
1775-
#endif /* !MS_WINDOWS || MS_WINDOWS_DESKTOP || MS_WINDOWS_GAMES */

PC/config.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@ extern PyObject* PyInit__collections(void);
4444
extern PyObject* PyInit__heapq(void);
4545
extern PyObject* PyInit__bisect(void);
4646
extern PyObject* PyInit__symtable(void);
47-
#if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_GAMES)
4847
extern PyObject* PyInit_mmap(void);
49-
#endif
5048
extern PyObject* PyInit__csv(void);
5149
extern PyObject* PyInit__sre(void);
5250
#if defined(MS_WINDOWS_DESKTOP) || defined(MS_WINDOWS_SYSTEM) || defined(MS_WINDOWS_GAMES)

0 commit comments

Comments
 (0)