Skip to content

Commit e325fae

Browse files
[3.15] gh-156126: Fix crash in -X importtime with unencodable module names (GH-156137) (#156259)
Co-authored-by: Kirill Podoprigora <kirill.bast9@mail.ru>
1 parent 6241129 commit e325fae

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
@@ -1267,6 +1267,24 @@ def test_import_time(self):
12671267
assert_python_failure('-X', 'importtime=-1', '-c', code)
12681268
assert_python_failure('-X', 'importtime=3', '-c', code)
12691269

1270+
def test_import_time_unencodable_module_name(self):
1271+
code = textwrap.dedent("""
1272+
import sys, types
1273+
name = 'mod\\ud800'
1274+
sys.modules[name] = types.ModuleType(name)
1275+
__import__(name)
1276+
try:
1277+
__import__('nonexistent\\ud800')
1278+
except ModuleNotFoundError:
1279+
pass
1280+
""")
1281+
res = assert_python_ok('-X', 'importtime=2', '-c', code)
1282+
res_err = res.err.decode('utf-8')
1283+
self.assertRegex(res_err,
1284+
r'import time: cached\s* \| cached\s* \| mod\\ud800')
1285+
self.assertRegex(res_err,
1286+
r'import time: \s*\d+ \| \s*\d+ \| \s*nonexistent\\ud800')
1287+
12701288
def res2int(self, res):
12711289
out = res.out.strip().decode("utf-8")
12721290
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
@@ -288,6 +288,19 @@ _PyImport_ClearLazyModules(PyInterpreterState *interp)
288288
Py_CLEAR(LAZY_PENDING_SUBMODULES(interp));
289289
}
290290

291+
static PyObject *
292+
get_importtime_name(PyObject *name)
293+
{
294+
PyObject *exc = PyErr_GetRaisedException();
295+
PyObject *encoded = PyUnicode_AsEncodedString(name, "utf-8",
296+
"backslashreplace");
297+
if (encoded == NULL) {
298+
PyErr_Clear();
299+
}
300+
PyErr_SetRaisedException(exc);
301+
return encoded;
302+
}
303+
291304
static int
292305
import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *name)
293306
{
@@ -325,8 +338,11 @@ import_ensure_initialized(PyInterpreterState *interp, PyObject *mod, PyObject *n
325338
if (_PyInterpreterState_GetConfig(interp)->import_time == 2) {
326339
_IMPORT_TIME_HEADER(interp);
327340
#define import_level FIND_AND_LOAD(interp).import_level
341+
PyObject *encoded_name = get_importtime_name(name);
328342
fprintf(stderr, "import time: cached | cached | %*s\n",
329-
import_level*2, PyUnicode_AsUTF8(name));
343+
import_level*2,
344+
encoded_name != NULL ? PyBytes_AS_STRING(encoded_name) : "?");
345+
Py_XDECREF(encoded_name);
330346
#undef import_level
331347
}
332348

@@ -4118,10 +4134,13 @@ import_find_and_load_with_name(PyThreadState *tstate, PyObject *abs_name,
41184134
PyTime_t cum = t2 - t1;
41194135

41204136
import_level--;
4137+
PyObject *encoded_name = get_importtime_name(abs_name);
41214138
fprintf(stderr, "import time: %9ld | %10ld | %*s%s\n",
41224139
(long)_PyTime_AsMicroseconds(cum - accumulated, _PyTime_ROUND_CEILING),
41234140
(long)_PyTime_AsMicroseconds(cum, _PyTime_ROUND_CEILING),
4124-
import_level*2, "", PyUnicode_AsUTF8(abs_name));
4141+
import_level*2, "",
4142+
encoded_name != NULL ? PyBytes_AS_STRING(encoded_name) : "?");
4143+
Py_XDECREF(encoded_name);
41254144

41264145
accumulated = accumulated_copy + cum;
41274146
}

0 commit comments

Comments
 (0)