Skip to content

Commit 0a467ba

Browse files
committed
[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 82d3439)
1 parent 69b7d35 commit 0a467ba

3 files changed

Lines changed: 54 additions & 25 deletions

File tree

‎Lib/multiprocessing/shared_memory.py‎

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -349,31 +349,40 @@ def __init__(self, sequence=None, *, name=None):
349349

350350
if sequence is not None:
351351
_enc = _encoding
352-
struct.pack_into(
353-
"q" + self._format_size_metainfo,
354-
self.shm.buf,
355-
0,
356-
self._list_len,
357-
*(self._allocated_offsets)
358-
)
359-
struct.pack_into(
360-
"".join(_formats),
361-
self.shm.buf,
362-
self._offset_data_start,
363-
*(self._encode_value(v) for v in sequence)
364-
)
365-
struct.pack_into(
366-
self._format_packing_metainfo,
367-
self.shm.buf,
368-
self._offset_packing_formats,
369-
*(v.encode(_enc) for v in _formats)
370-
)
371-
struct.pack_into(
372-
self._format_back_transform_codes,
373-
self.shm.buf,
374-
self._offset_back_transform_codes,
375-
*(_recreation_codes)
376-
)
352+
try:
353+
struct.pack_into(
354+
"q" + self._format_size_metainfo,
355+
self.shm.buf,
356+
0,
357+
self._list_len,
358+
*(self._allocated_offsets)
359+
)
360+
struct.pack_into(
361+
"".join(_formats),
362+
self.shm.buf,
363+
self._offset_data_start,
364+
*(self._encode_value(v) for v in sequence)
365+
)
366+
struct.pack_into(
367+
self._format_packing_metainfo,
368+
self.shm.buf,
369+
self._offset_packing_formats,
370+
*(v.encode(_enc) for v in _formats)
371+
)
372+
struct.pack_into(
373+
self._format_back_transform_codes,
374+
self.shm.buf,
375+
self._offset_back_transform_codes,
376+
*(_recreation_codes)
377+
)
378+
except BaseException:
379+
# A value of a supported type may still be unpackable, such as
380+
# an int outside the signed 64-bit range. The block was created
381+
# above and the caller gets no object to release it with, so
382+
# destroy it here.
383+
self.shm.close()
384+
self.shm.unlink()
385+
raise
377386

378387
else:
379388
self._list_len = len(self) # Obtains size from offset 0 in buffer.

‎Lib/test/_test_multiprocessing.py‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4922,6 +4922,23 @@ def test_shared_memory_ShareableList_basics(self):
49224922
finally:
49234923
empty_sl.shm.unlink()
49244924

4925+
def test_shared_memory_ShareableList_unpackable_value(self):
4926+
# Values of a supported type that cannot be packed must not leave
4927+
# the newly created shared memory block behind.
4928+
name = self._new_shm_name('test03_unpackable')
4929+
for value, exc in (
4930+
(2**63, struct.error),
4931+
(-2**63 - 1, struct.error),
4932+
):
4933+
with self.subTest(value=value):
4934+
with self.assertRaises(exc):
4935+
shared_memory.ShareableList(['abc', value], name=name)
4936+
if sys.platform != "win32":
4937+
with self.assertRaises(FileNotFoundError):
4938+
# Should not be there to be attached to.
4939+
leftover = shared_memory.SharedMemory(name)
4940+
leftover.unlink() # Error should occur on prior line.
4941+
49254942
def test_shared_memory_ShareableList_pickling(self):
49264943
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
49274944
with self.subTest(proto=proto):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix :class:`multiprocessing.shared_memory.ShareableList` leaving its newly
2+
created shared memory block behind when a value cannot be stored, such as an
3+
integer outside the signed 64-bit range.

0 commit comments

Comments
 (0)