python / python/cpython

multiprocessing SharedMemory.__del__ can fail, leak file descriptor

Open
#155,003 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

stdlib topic-multiprocessing type-bug
Dominant language
Python
Stars
77.2k
Forks
35.9k
PR merge metrics
PR metrics pending

Description

Bug report

Bug description:

When a SharedMemory object goes out of scope, its __del__ method calls self.close() to clean it up. This can break if there is still a buffer exported from its mmap instance. It tries to close the mmap before the fd, so in this case it will leave the file descriptor open:

>>> from multiprocessing.shared_memory import SharedMemory
>>> import os
>>> def shview(size=4096):
...     shmem = SharedMemory(create=True, size=size)
...     print("fd is:", shmem._fd)
...     return shmem.buf.cast('i')
...     
>>> mv = shview()
fd is: 5
Exception ignored while calling deallocator <function SharedMemory.__del__ at 0x7f686264d640>:
Traceback (most recent call last):
  File "/usr/lib64/python3.14/multiprocessing/shared_memory.py", line 189, in __del__
    self.close()
  File "/usr/lib64/python3.14/multiprocessing/shared_memory.py", line 232, in close
    self._mmap.close()
BufferError: cannot close exported pointers exist
>>> os.fstat(5).st_size  # fd still open
4096

I think a better option for __del__ would be to close the fd and leave the mmap & buf objects alone, to be cleaned up as normal by reference counting or GC:

>>> class SharedMemoryFix(SharedMemory):
...     def __del__(self):
...         if self._fd >= 0:
...             os.close(self._fd)
...             self._fd = -1
...             
>>> def shview(size=4096):
...     shmem = SharedMemoryFix(create=True, size=size)
...     print("fd is:", shmem._fd)
...     return shmem.buf.cast('i')
...     
>>> mv = shview()
fd is: 7
>>> os.fstat(7)
Traceback (most recent call last):
  File "<python-input-8>", line 1, in <module>
    os.fstat(7)
    ~~~~~~~~^^^
OSError: [Errno 9] Bad file descriptor

(I'm thinking about the POSIX side here. IDK if the behaviour on Windows needs to be different, though I guess it can rely on the mmap's dealloc too)

CPython versions tested on:

3.14

Operating systems tested on:

Linux

Linked PRs
  • gh-155007
  • gh-155070

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in Lib/multiprocessing/shared_memory.py at SharedMemory.del and close(), using the Linux reproduction in the issue to observe the exported-buffer failure and leaked file descriptor. Check the linked PRs gh-155007 and gh-155070 before beginning; done means the destructor no longer leaves the descriptor open while preserving the relevant mmap and buffer behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
operating-systems
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.