From 0a467bae7a11c08e5e104b4deb61595cfdacc459 Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" <68491+gpshead@users.noreply.github.com> Date: Sun, 1 Nov 2026 10:35:40 -0800 Subject: [PATCH] [3.15] gh-161098: Unlink the new ShareableList block if packing a value fails (GH-161099) ShareableList(sequence) created its shared memory block before packing the values, so an unpackable value (an int outside int64, a lone surrogate) left the block behind with no way for the caller to release it. (cherry picked from commit 82d3439f7cc243b5f8a82d3d1c5c03ad03f8ff80) --- Lib/multiprocessing/shared_memory.py | 59 +++++++++++-------- Lib/test/_test_multiprocessing.py | 17 ++++++ ...-10-22-05-20-27.gh-issue-161098.voMVnd.rst | 3 + 3 files changed, 54 insertions(+), 25 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-22-05-20-27.gh-issue-161098.voMVnd.rst diff --git a/Lib/multiprocessing/shared_memory.py b/Lib/multiprocessing/shared_memory.py index 582a00a32facff7..b5396451d0e64a5 100644 --- a/Lib/multiprocessing/shared_memory.py +++ b/Lib/multiprocessing/shared_memory.py @@ -349,31 +349,40 @@ def __init__(self, sequence=None, *, name=None): if sequence is not None: _enc = _encoding - struct.pack_into( - "q" + self._format_size_metainfo, - self.shm.buf, - 0, - self._list_len, - *(self._allocated_offsets) - ) - struct.pack_into( - "".join(_formats), - self.shm.buf, - self._offset_data_start, - *(self._encode_value(v) for v in sequence) - ) - struct.pack_into( - self._format_packing_metainfo, - self.shm.buf, - self._offset_packing_formats, - *(v.encode(_enc) for v in _formats) - ) - struct.pack_into( - self._format_back_transform_codes, - self.shm.buf, - self._offset_back_transform_codes, - *(_recreation_codes) - ) + try: + struct.pack_into( + "q" + self._format_size_metainfo, + self.shm.buf, + 0, + self._list_len, + *(self._allocated_offsets) + ) + struct.pack_into( + "".join(_formats), + self.shm.buf, + self._offset_data_start, + *(self._encode_value(v) for v in sequence) + ) + struct.pack_into( + self._format_packing_metainfo, + self.shm.buf, + self._offset_packing_formats, + *(v.encode(_enc) for v in _formats) + ) + struct.pack_into( + self._format_back_transform_codes, + self.shm.buf, + self._offset_back_transform_codes, + *(_recreation_codes) + ) + except BaseException: + # A value of a supported type may still be unpackable, such as + # an int outside the signed 64-bit range. The block was created + # above and the caller gets no object to release it with, so + # destroy it here. + self.shm.close() + self.shm.unlink() + raise else: self._list_len = len(self) # Obtains size from offset 0 in buffer. diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index 87e13b05e1bfe9d..f2b666570d88724 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -4922,6 +4922,23 @@ def test_shared_memory_ShareableList_basics(self): finally: empty_sl.shm.unlink() + def test_shared_memory_ShareableList_unpackable_value(self): + # Values of a supported type that cannot be packed must not leave + # the newly created shared memory block behind. + name = self._new_shm_name('test03_unpackable') + for value, exc in ( + (2**63, struct.error), + (-2**63 - 1, struct.error), + ): + with self.subTest(value=value): + with self.assertRaises(exc): + shared_memory.ShareableList(['abc', value], name=name) + if sys.platform != "win32": + with self.assertRaises(FileNotFoundError): + # Should not be there to be attached to. + leftover = shared_memory.SharedMemory(name) + leftover.unlink() # Error should occur on prior line. + def test_shared_memory_ShareableList_pickling(self): for proto in range(pickle.HIGHEST_PROTOCOL + 1): with self.subTest(proto=proto): diff --git a/Misc/NEWS.d/next/Library/2026-10-22-05-20-27.gh-issue-161098.voMVnd.rst b/Misc/NEWS.d/next/Library/2026-10-22-05-20-27.gh-issue-161098.voMVnd.rst new file mode 100644 index 000000000000000..73849a0136aafcc --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-22-05-20-27.gh-issue-161098.voMVnd.rst @@ -0,0 +1,3 @@ +Fix :class:`multiprocessing.shared_memory.ShareableList` leaving its newly +created shared memory block behind when a value cannot be stored, such as an +integer outside the signed 64-bit range.