Skip to content

Commit 7267c9b

Browse files
authored
[3.14] gh-156126: Fix crash in -X importtime with unencodable module … (#156330)
[3.14] gh-156126: Fix crash in -X importtime with unencodable module names (GH-156137) (cherry picked from commit 3a5aa68)
1 parent 7cc0932 commit 7267c9b

3 files changed

Lines changed: 42 additions & 2 deletions

File tree

Lib/test/test_cmd_line.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,24 @@ def test_import_time(self):
12251225
assert_python_failure('-X', 'importtime=-1', '-c', code)
12261226
assert_python_failure('-X', 'importtime=3', '-c', code)
12271227

1228+
def test_import_time_unencodable_module_name(self):
1229+
code = textwrap.dedent("""
1230+
import sys, types
1231+
name = 'mod\\ud800'
1232+
sys.modules[name] = types.ModuleType(name)
1233+
__import__(name)
1234+
try:
1235+
__import__('nonexistent\\ud800')
1236+
except ModuleNotFoundError:
1237+
pass
1238+
""")
1239+
res = assert_python_ok('-X', 'importtime=2', '-c', code)
1240+
res_err = res.err.decode('utf-8')
1241+
self.assertRegex(res_err,
1242+
r'import time: cached\s* \| cached\s* \| mod\\ud800')
1243+
self.assertRegex(res_err,
1244+
r'import time: \s*\d+ \| \s*\d+ \| \s*nonexistent\\ud800')
1245+
12281246
def res2int(self, res):
12291247
out = res.out.strip().decode("utf-8")
12301248
return tuple(int(i) for i in out.split())
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a crash when importing a module whose name contains characters that
2+
cannot be encoded to UTF-8 (such as lone surrogates) while :option:`-X
3+
importtime <-X>` is enabled.

Python/import.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,19 @@ import_get_module(PyThreadState *tstate, PyObject *name)
241241
return m;
242242
}
243243

244+
static PyObject *
245+
get_importtime_name(PyObject *name)
246+
{
247+
PyObject *exc = PyErr_GetRaisedException();
248+
PyObject *encoded = PyUnicode_AsEncodedString(name, "utf-8",
249+
"backslashreplace");
250+
if (encoded == NULL) {
251+
PyErr_Clear();
252+
}
253+
PyErr_SetRaisedException(exc);
254+
return encoded;
255+
}
256+
244257
static int
245258
import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *name)
246259
{
@@ -278,8 +291,11 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n
278291
if (_PyInterpreterState_GetConfig(interp)->import_time == 2) {
279292
_IMPORT_TIME_HEADER(interp);
280293
#define import_level FIND_AND_LOAD(interp).import_level
294+
PyObject *encoded_name = get_importtime_name(name);
281295
fprintf(stderr, "import time: cached | cached | %*s\n",
282-
import_level*2, PyUnicode_AsUTF8(name));
296+
import_level*2,
297+
encoded_name != NULL ? PyBytes_AS_STRING(encoded_name) : "?");
298+
Py_XDECREF(encoded_name);
283299
#undef import_level
284300
}
285301

@@ -3782,10 +3798,13 @@ import_find_and_load(PyThreadState *tstate, PyObject *abs_name)
37823798
PyTime_t cum = t2 - t1;
37833799

37843800
import_level--;
3801+
PyObject *encoded_name = get_importtime_name(abs_name);
37853802
fprintf(stderr, "import time: %9ld | %10ld | %*s%s\n",
37863803
(long)_PyTime_AsMicroseconds(cum - accumulated, _PyTime_ROUND_CEILING),
37873804
(long)_PyTime_AsMicroseconds(cum, _PyTime_ROUND_CEILING),
3788-
import_level*2, "", PyUnicode_AsUTF8(abs_name));
3805+
import_level*2, "",
3806+
encoded_name != NULL ? PyBytes_AS_STRING(encoded_name) : "?");
3807+
Py_XDECREF(encoded_name);
37893808

37903809
accumulated = accumulated_copy + cum;
37913810
}

0 commit comments

Comments
 (0)