Skip to content

Commit 1ddf61b

Browse files
authored
Merge branch 'main' into fix-universal2-trampoline
2 parents 1d010ee + 43c60ec commit 1ddf61b

4 files changed

Lines changed: 28 additions & 23 deletions

File tree

Lib/test/test_unicodedata.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,22 @@ def test_failed_import_during_compiling(self):
11061106
"(can't load unicodedata module)"
11071107
self.assertIn(error, result.err.decode("ascii"))
11081108

1109+
def test_unicodedata_unload_reload(self):
1110+
# gh-149449: dropping unicodedata and running gc must not leave the
1111+
# cached _ucnhash_CAPI pointer dangling.
1112+
code = (
1113+
"import gc, sys\n"
1114+
"assert '\\N{GRINNING FACE}'.encode("
1115+
" 'ascii', errors='namereplace') == b'\\\\N{GRINNING FACE}'\n"
1116+
"compile(r\"x = '\\\\N{LATIN CAPITAL LETTER A}'\", '<x>', 'exec')\n"
1117+
"del sys.modules['unicodedata']\n"
1118+
"gc.collect()\n"
1119+
"assert '\\N{WINKING FACE}'.encode("
1120+
" 'ascii', errors='namereplace') == b'\\\\N{WINKING FACE}'\n"
1121+
"compile(r\"x = '\\\\N{LATIN CAPITAL LETTER B}'\", '<x>', 'exec')\n"
1122+
)
1123+
script_helper.assert_python_ok("-c", code)
1124+
11091125
def test_decimal_numeric_consistent(self):
11101126
# Test that decimal and numeric are consistent,
11111127
# i.e. if a character has a decimal value,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix a use-after-free crash when the :mod:`unicodedata` module was removed
2+
from :data:`sys.modules` and garbage-collected between calls that decode
3+
``\N{...}`` escapes or use the ``namereplace`` codec error handler.

Modules/unicodedata.c

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1548,32 +1548,17 @@ capi_getcode(const char* name, int namelen, Py_UCS4* code,
15481548
return _check_alias_and_seq(code, with_named_seq);
15491549
}
15501550

1551-
static void
1552-
unicodedata_destroy_capi(PyObject *capsule)
1553-
{
1554-
void *capi = PyCapsule_GetPointer(capsule, PyUnicodeData_CAPSULE_NAME);
1555-
PyMem_Free(capi);
1556-
}
1557-
15581551
static PyObject *
15591552
unicodedata_create_capi(void)
15601553
{
1561-
_PyUnicode_Name_CAPI *capi = PyMem_Malloc(sizeof(_PyUnicode_Name_CAPI));
1562-
if (capi == NULL) {
1563-
PyErr_NoMemory();
1564-
return NULL;
1565-
}
1566-
capi->getname = capi_getucname;
1567-
capi->getcode = capi_getcode;
1568-
1569-
PyObject *capsule = PyCapsule_New(capi,
1570-
PyUnicodeData_CAPSULE_NAME,
1571-
unicodedata_destroy_capi);
1572-
if (capsule == NULL) {
1573-
PyMem_Free(capi);
1574-
}
1575-
return capsule;
1576-
};
1554+
// Statically allocated so that any cached pointers stay valid after unicodedata
1555+
// is removed from sys.modules and the capsule is gc'd (gh-149449).
1556+
static _PyUnicode_Name_CAPI capi = {
1557+
.getname = capi_getucname,
1558+
.getcode = capi_getcode,
1559+
};
1560+
return PyCapsule_New(&capi, PyUnicodeData_CAPSULE_NAME, NULL);
1561+
}
15771562

15781563

15791564
/* -------------------------------------------------------------------- */

Tools/c-analyzer/cpython/ignored.tsv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ Modules/pyexpat.c - error_info_of -
327327
Modules/pyexpat.c - handler_info -
328328
Modules/termios.c - termios_constants -
329329
Modules/timemodule.c init_timezone YEAR -
330+
Modules/unicodedata.c unicodedata_create_capi capi -
330331
Objects/bytearrayobject.c - _PyByteArray_empty_string -
331332
Objects/complexobject.c - c_1 -
332333
Objects/exceptions.c - static_exceptions -

0 commit comments

Comments
 (0)