Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions Lib/test/test_unicodedata.py
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,22 @@ def test_failed_import_during_compiling(self):
"(can't load unicodedata module)"
self.assertIn(error, result.err.decode("ascii"))

def test_unicodedata_unload_reload(self):
# gh-149449: dropping unicodedata and running gc must not leave the
# cached _ucnhash_CAPI pointer dangling.
code = (
"import gc, sys\n"
"assert '\\N{GRINNING FACE}'.encode("
" 'ascii', errors='namereplace') == b'\\\\N{GRINNING FACE}'\n"
"compile(r\"x = '\\\\N{LATIN CAPITAL LETTER A}'\", '<x>', 'exec')\n"
"del sys.modules['unicodedata']\n"
"gc.collect()\n"
"assert '\\N{WINKING FACE}'.encode("
" 'ascii', errors='namereplace') == b'\\\\N{WINKING FACE}'\n"
"compile(r\"x = '\\\\N{LATIN CAPITAL LETTER B}'\", '<x>', 'exec')\n"
)
script_helper.assert_python_ok("-c", code)

def test_decimal_numeric_consistent(self):
# Test that decimal and numeric are consistent,
# i.e. if a character has a decimal value,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Fix a use-after-free crash when the :mod:`unicodedata` module was removed
from :data:`sys.modules` and garbage-collected between calls that decode
``\N{...}`` escapes or use the ``namereplace`` codec error handler.
31 changes: 8 additions & 23 deletions Modules/unicodedata.c
Original file line number Diff line number Diff line change
Expand Up @@ -1475,7 +1475,7 @@
}

if (i < (int)Py_ARRAY_LENGTH(derived_name_prefixes)) {
Py_UCS4 v = parse_hex_code(name + prefixlen, namelen - prefixlen);

Check warning on line 1478 in Modules/unicodedata.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (arm64)

'function': conversion from 'size_t' to 'int', possible loss of data [C:\a\cpython\cpython\PCbuild\unicodedata.vcxproj]

Check warning on line 1478 in Modules/unicodedata.c

View workflow job for this annotation

GitHub Actions / Windows / Build and test (x64)

'function': conversion from 'size_t' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\unicodedata.vcxproj]

Check warning on line 1478 in Modules/unicodedata.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (x64)

'function': conversion from 'size_t' to 'int', possible loss of data [D:\a\cpython\cpython\PCbuild\unicodedata.vcxproj]

Check warning on line 1478 in Modules/unicodedata.c

View workflow job for this annotation

GitHub Actions / Windows (free-threading) / Build and test (arm64)

'function': conversion from 'size_t' to 'int', possible loss of data [C:\a\cpython\cpython\PCbuild\unicodedata.vcxproj]
if (find_prefix_id(v) != i) {
return 0;
}
Expand Down Expand Up @@ -1503,32 +1503,17 @@
return _check_alias_and_seq(code, with_named_seq);
}

static void
unicodedata_destroy_capi(PyObject *capsule)
{
void *capi = PyCapsule_GetPointer(capsule, PyUnicodeData_CAPSULE_NAME);
PyMem_Free(capi);
}

static PyObject *
unicodedata_create_capi(void)
{
_PyUnicode_Name_CAPI *capi = PyMem_Malloc(sizeof(_PyUnicode_Name_CAPI));
if (capi == NULL) {
PyErr_NoMemory();
return NULL;
}
capi->getname = capi_getucname;
capi->getcode = capi_getcode;

PyObject *capsule = PyCapsule_New(capi,
PyUnicodeData_CAPSULE_NAME,
unicodedata_destroy_capi);
if (capsule == NULL) {
PyMem_Free(capi);
}
return capsule;
};
// Statically allocated so that any cached pointers stay valid after unicodedata
// is removed from sys.modules and the capsule is gc'd (gh-149449).
static _PyUnicode_Name_CAPI capi = {
.getname = capi_getucname,
.getcode = capi_getcode,
};
return PyCapsule_New(&capi, PyUnicodeData_CAPSULE_NAME, NULL);
}


/* -------------------------------------------------------------------- */
Expand Down
1 change: 1 addition & 0 deletions Tools/c-analyzer/cpython/ignored.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ Modules/pyexpat.c - error_info_of -
Modules/pyexpat.c - handler_info -
Modules/termios.c - termios_constants -
Modules/timemodule.c init_timezone YEAR -
Modules/unicodedata.c unicodedata_create_capi capi -
Objects/bytearrayobject.c - _PyByteArray_empty_string -
Objects/complexobject.c - c_1 -
Objects/exceptions.c - static_exceptions -
Expand Down
Loading