Skip to content

Commit 4549146

Browse files
committed
fix: allow lone surrogates in module filenames for crossinterp (gh-156122)
1 parent 918fb3a commit 4549146

4 files changed

Lines changed: 39 additions & 9 deletions

File tree

Lib/test/test_interpreters/test_api.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,6 +1683,25 @@ def get_count():
16831683
self.assertEqual(after, 0)
16841684
self.assertEqual(counts, [0, 1, 4])
16851685

1686+
def test_surrogate_filename_in___main__(self):
1687+
interp = interpreters.create()
1688+
import __main__
1689+
orig_file = getattr(__main__, '__file__', None)
1690+
try:
1691+
for surrogate in ('\ud800', '\udcff'):
1692+
with self.subTest(surrogate=ascii(surrogate)):
1693+
__main__.__file__ = f'my_script_{surrogate}.py'
1694+
res = interp.call(lambda x: x, [1])
1695+
self.assertEqual(res, [1])
1696+
finally:
1697+
if orig_file is None:
1698+
try:
1699+
del __main__.__file__
1700+
except AttributeError:
1701+
pass
1702+
else:
1703+
__main__.__file__ = orig_file
1704+
16861705
def test_raises(self):
16871706
interp = interpreters.create()
16881707
with self.assertRaises(ExecutionFailed):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash when module filenames containing lone surrogates are used during
2+
cross-interpreter unpickling.

Objects/moduleobject.c

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -991,14 +991,17 @@ _PyModule_GetFilenameUTF8(PyObject *mod, char *buffer, Py_ssize_t maxlen)
991991
size = 0;
992992
}
993993
else {
994-
const char *filename = PyUnicode_AsUTF8AndSize(filenameobj, &size);
995-
assert(size >= 0);
996-
if (size > maxlen) {
997-
size = -1;
998-
PyErr_SetString(PyExc_ValueError, "__file__ too long");
999-
}
1000-
else {
1001-
(void)strcpy(buffer, filename);
994+
PyObject *bytes = PyUnicode_EncodeFSDefault(filenameobj);
995+
if (bytes != NULL) {
996+
size = PyBytes_GET_SIZE(bytes);
997+
if (size > maxlen) {
998+
size = -1;
999+
PyErr_SetString(PyExc_ValueError, "__file__ too long");
1000+
}
1001+
else {
1002+
memcpy(buffer, PyBytes_AS_STRING(bytes), size + 1);
1003+
}
1004+
Py_DECREF(bytes);
10021005
}
10031006
}
10041007
Py_DECREF(filenameobj);

Python/crossinterp.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,13 @@ runpy_run_path(const char *filename, const char *modname)
4141
if (run_path == NULL) {
4242
return NULL;
4343
}
44-
PyObject *args = Py_BuildValue("(sOs)", filename, Py_None, modname);
44+
PyObject *path = PyUnicode_DecodeFSDefault(filename);
45+
if (path == NULL) {
46+
Py_DECREF(run_path);
47+
return NULL;
48+
}
49+
PyObject *args = Py_BuildValue("(OOs)", path, Py_None, modname);
50+
Py_DECREF(path);
4551
if (args == NULL) {
4652
Py_DECREF(run_path);
4753
return NULL;

0 commit comments

Comments
 (0)