From e62d5c61911f5e6b005babfd348e535550d93893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?tonghuaroot=20=28=E7=AB=A5=E8=AF=9D=29?= Date: Sat, 24 Oct 2026 14:57:04 +0800 Subject: [PATCH] gh-139122: Fix crash when pickling a C UUID without is_safe UUID.__getstate__() passed a NULL is_safe to PyDict_SetItem() for every UUID created without is_safe. Omit the key, as the Python version does for SafeUUID.unknown. --- Lib/test/test_uuid.py | 20 ++++++++++++++++++++ Modules/_uuidmodule.c | 5 ++++- 2 files changed, 24 insertions(+), 1 deletion(-) mode change 100755 => 100644 Lib/test/test_uuid.py diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py old mode 100755 new mode 100644 index ee6e57b93966a2..105d7c43409b70 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -495,6 +495,26 @@ def check(actual, expected): with self.subTest(protocol=proto): check(pickle.loads(pickle.dumps(u, proto)), u) + def test_pickle_roundtrip_default_is_safe(self): + def check(actual, expected): + self.assertEqual(actual, expected) + self.assertEqual(actual.is_safe, expected.is_safe) + + with support.swap_item(sys.modules, 'uuid', self.uuid): + uuids = [ + self.uuid.UUID('d82579ce6642a0de7ddf490a7aec7aa5'), + self.uuid.UUID(int=1), + self.uuid.uuid4(), + self.uuid.uuid7(), + ] + for u in uuids: + with self.subTest(u=u): + self.assertEqual(u.is_safe, self.uuid.SafeUUID.unknown) + check(copy.copy(u), u) + check(copy.deepcopy(u), u) + for proto in range(pickle.HIGHEST_PROTOCOL + 1): + check(pickle.loads(pickle.dumps(u, proto)), u) + def test_setstate_mapping(self): # State restoration accepts mappings and decodes serialized safety values. safety_states = [({}, self.uuid.SafeUUID.unknown)] diff --git a/Modules/_uuidmodule.c b/Modules/_uuidmodule.c index e957071a4ecef9..00691aa5fd4c50 100644 --- a/Modules/_uuidmodule.c +++ b/Modules/_uuidmodule.c @@ -1621,7 +1621,10 @@ _uuid_UUID___getstate___impl(uuidobject *self) } Py_DECREF(int_value); - if (PyDict_SetItem(dict, &_Py_ID(is_safe), self->is_safe) < 0) { + // NULL means SafeUUID.unknown, which __setstate__() restores by default. + if (self->is_safe != NULL && + PyDict_SetItem(dict, &_Py_ID(is_safe), self->is_safe) < 0) + { Py_DECREF(dict); return NULL; }