Skip to content

BUG: Got Segmentation Fault when use-after-free a ndarray using SharedMemory as its buffer #23305

Open

Description

@knwng

Describe the issue:

Hi, I encountered a seg fault using ndarray with SharedMemory as its buffer, as discussed in https://discuss.python.org/t/should-sharedmemory-close-be-called-explicitly-or-we-should-just-rely-on-del/24281/1

Reproduce the code example:

import numpy as np
from multiprocessing.shared_memory import SharedMemory

class Block(object):
    def __init__(self, nbytes, shape, dtype):
        self._nbytes = nbytes
        self._shape = shape
        self._dtype = dtype

    def get_ndarray(self):
        shm = SharedMemory(name=self._shm_name)
        return np.ndarray(self._shape, dtype=self._dtype, buffer=shm.buf)

    def set_ndarray(self, arr):
        shm = SharedMemory(create=True, size=self._nbytes)
        shm_arr = np.ndarray(self._shape, dtype=self._dtype, buffer=shm.buf)
        shm_arr[:] = arr[:]
        self._shm_name = shm.name

arr = np.random.rand(1, 100)
block = Block(arr.nbytes, arr.shape, arr.dtype)
block.set_ndarray(arr)
arr_on_shm = block.get_ndarray()
print(f'arr_on_shm: {arr_on_shm}')

Error message:

[1]    13006 segmentation fault  python3 test_shm_numpy.py

Runtime information:

import sys, numpy; print(numpy.version); print(sys.version)
1.24.2
3.11.1 (main, Dec 23 2022, 09:39:26) [Clang 14.0.0 (clang-1400.0.29.202)]
print(numpy.show_runtime())
WARNING: threadpoolctl not found in system! Install it by pip install threadpoolctl. Once installed, try np.show_runtime again for more detailed build information
[{'simd_extensions': {'baseline': ['SSE', 'SSE2', 'SSE3'],
'found': ['SSSE3',
'SSE41',
'POPCNT',
'SSE42',
'AVX',
'F16C',
'FMA3',
'AVX2'],
'not_found': ['AVX512F',
'AVX512CD',
'AVX512_KNL',
'AVX512_SKX',
'AVX512_CLX',
'AVX512_CNL',
'AVX512_ICL']}}]
None

Context for the issue:

No response

Activity

  1. knwng commented on Mar 1, 2023

    @knwng
    Author

    If I visit the SharedMemory.buf(which is a memoryview of mmap) after shm is released, I'll got a ValueError: operation forbidden on released memoryview object exception instead a seg fault. But somehow this kind of guard doesn't work in ndarray. Is it possible for ndarray to check the status of memoryview before visiting?

  2. seberg commented on Mar 6, 2023

    @seberg
    Member

    For what its worth np.frombuffer should protect you here, maybe that should be expanded to np.ndarray.

  3. knwng commented on Mar 6, 2023

    @knwng
    Author

    Hi @seberg , I tried to use np.frombuffer and the seg fault disappeared. But when the SharedMemory is destroyed, the __del__method raise a BufferError that can't be caught.

    The code is:

    import numpy as np
    from multiprocessing.shared_memory import SharedMemory
    
    class Block(object):
        def __init__(self, nbytes, shape, dtype):
            self._nbytes = nbytes
            self._shape = shape
            self._dtype = dtype
            self._numel = 1
            for s in self._shape:
                self._numel *= s
    
        def get_ndarray(self):
            shm = SharedMemory(name=self._shm_name)
            return np.frombuffer(shm.buf, dtype=self._dtype, count=self._numel).reshape(self._shape)
    
        def set_ndarray(self, arr):
            shm = SharedMemory(create=True, size=self._nbytes)
            shm_arr = np.ndarray(self._shape, dtype=self._dtype, buffer=shm.buf)
            shm_arr[:] = arr[:]
            self._shm_name = shm.name
    
    arr = np.random.rand(1, 100)
    block = Block(arr.nbytes, arr.shape, arr.dtype)
    block.set_ndarray(arr)
    arr_on_shm = block.get_ndarray()
    print(f'arr_on_shm: {arr_on_shm}')

    The message printed:

    $ python test_shm_numpy.py
    Exception ignored in: <function SharedMemory.__del__ at 0x10d704ca0>
    Traceback (most recent call last):
      File "/usr/local/Cellar/[email protected]/3.10.2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/multiprocessing/shared_memory.py", line 184, in __del__
        self.close()
      File "/usr/local/Cellar/[email protected]/3.10.2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/multiprocessing/shared_memory.py", line 227, in close
        self._mmap.close()
    BufferError: cannot close exported pointers exist
    arr_on_shm: [[0.79792792 0.04141802 0.32935948 0.15143366 0.90435374 0.37330291
      0.60584314 0.63479729 0.79978235 0.68428578 0.26513576 0.04648537
      0.00560942 0.56813512 0.73790891 0.53200346 0.68997211 0.51508927
      0.83312322 0.42750275 0.93084579 0.32110023 0.60055336 0.24697209
      0.77778048 0.27429661 0.9451664  0.0674227  0.65624786 0.9606804
      0.56553997 0.94716262 0.63812559 0.49843126 0.55652149 0.77116879
      0.51142298 0.3394056  0.26521885 0.65057431 0.9937414  0.22389928
      0.07379827 0.16966286 0.18775718 0.93475792 0.86778178 0.65592673
      0.67815566 0.32770445 0.59924857 0.2514906  0.08924751 0.22847966
      0.45462247 0.61342018 0.23604664 0.11526659 0.23175976 0.55483772
      0.64609309 0.17542198 0.67517446 0.92351791 0.78805934 0.05538517
      0.1476812  0.64995614 0.43635739 0.12131003 0.94777382 0.80364381
      0.98159048 0.90066022 0.86832006 0.50155604 0.93980684 0.27602692
      0.20208085 0.58658399 0.40265376 0.04408677 0.67830517 0.34304762
      0.97313988 0.80070557 0.33092371 0.54287859 0.18326794 0.06128373
      0.20334361 0.39130072 0.68847418 0.48181348 0.75350679 0.07898048
      0.16453483 0.13452573 0.51182952 0.10095642]]
    /usr/local/Cellar/[email protected]/3.10.2/Frameworks/Python.framework/Versions/3.10/lib/python3.10/multiprocessing/resource_tracker.py:224: UserWarning: resource_tracker: There appear to be 1 leaked shared_memory objects to clean up at shutdown
      warnings.warn('resource_tracker: There appear to be %d '
    

    It's great to avoid the seg fault but the BufferError seems to be another problem. I'm not sure if it can be eliminated.

  4. seberg commented on Mar 6, 2023

    @seberg
    Member

    Maybe you should open it as a shared memory bug. NumPy cannot do more than memoryview and that is what you are seeing there. So if that is an issue, it is a SharedMemory issue, not a NumPy one.

    Maybe it is just unintended use, I guess, but either way it isn't NumPy. The only thing that may be plausible would be to have a numpy helper to create a shared memory array.

  5. knwng commented on Mar 7, 2023

    @knwng
    Author

    I was also struggling to classify this problem so at the beginning I reported an issue to cpython. But as @sobolevn said, I can't create a reproducable code without numpy since a normally released memoryview will through a catchable ValueError. Maybe it needs some cooperations between the two projects to determine whether this should be fixed and how?

  6. seberg commented on Mar 7, 2023

    @seberg
    Member

    The segfault is NumPy of course (there is historic context for this, but that doesn't matter much).

    But you can reproduce the the same thing as frombuffer by simply replacing it with memoryview.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions