This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Deallocation scheme for memoryview is unsafe
Type: behavior Stage: resolved
Components: Interpreter Core Versions: Python 3.6, Python 3.4, Python 3.5
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: martin.panter, serhiy.storchaka, skrah
Priority: normal Keywords: patch

Created on 2015-10-31 19:14 by serhiy.storchaka, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
memoryview_safer_clearing.patch serhiy.storchaka, 2015-11-01 13:56 review
Messages (11)
msg253803 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-10-31 19:14
Deallocation scheme for memoryview is complex and unsafe. It crashes with chained memoryviews (issue25498), but I suppose deallocating unchained memoryview can crash too if the memoryview itself had exported buffers (self->exports != 0).

Both memoryview and ManagedBuffer support garbage collector. If there is a reference to memoryview from reference loop, memoryview becomes a part of the reference loop.

    refloop -> memoryview -> ManagedBuffer -> obj

First garbage collector calls tp_clear for all objects in the loop (memory_clear() for memoryview).
If self->exports != 0 for memoryview, _memory_release() fails and the _Py_MEMORYVIEW_RELEASED flag is not set. However following Py_CLEAR(self->mbuf) deallocates ManagedBuffer and set self->mbuf to NULL. Then memoryview's owner releases memoryview, and it is deallocated (memory_dealloc). _memory_release reads self->mbuf->exports, but self->mbuf is NULL. Segmentation fault.

Following patch makes deallocation scheme for memoryview simpler and more reliable.

1) memory_clear does nothing if self->exports != 0. The buffer will be released when memoryview's owner release the memoryview.

2) ManagedBuffer no longer supports garbage collector. This prevents buffer releasing before memoryview or its owner are cleared.
msg253806 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2015-10-31 20:40
The "chained memoryviews" you refer to are a hack and simply
aren't supported. Please stop spreading FUD.
msg253807 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-10-31 20:47
Did you forget your patch? :)
msg253812 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-10-31 21:59
Yes, the "chained memoryviews" in ctypes are a hack and it will be eliminated. But this hack exposed possible weak point in memoryview.
msg253825 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-11-01 05:56
WTF? Where is my patch?
msg253826 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-11-01 06:07
Strange bug. I can't attach any file.
msg253839 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2015-11-01 10:16
> First garbage collector calls tp_clear for all objects in the loop (memory_clear() for memoryview).
If self->exports != 0 for memoryview, _memory_release() fails and the _Py_MEMORYVIEW_RELEASED flag is not set.

I don't understand:

  1) memory_clear()

  2) _memory_release()

  3) if self->exports > 0 then BufferError.


Please state the code path clearly.
msg253843 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-11-01 14:00
1) memory_clear()

  2) _memory_release()

  3) if self->exports > 0 then BufferError (but ignored).

  4) Py_CLEAR(self->mbuf)

  5) memory_dealloc()

  6) _memory_release()

  7) self->mbuf->exports where self->mbuf == NULL
msg253861 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2015-11-01 17:29
First of all, the premise "exports > 0" in your example looks wrong
to me.  The deallocation process for the first view should start precisely when it no longer has any exports.

In fact, the check for "exports > 0" is for the case when
memoryview.release() is called from the Python level.


Secondly, even if it did happen (show the code path leading
to that!), BufferError would be set in memory_clear() and the
garbage collector would throw a FatalError, i.e. step 5+ would
not be reached.


Lastly, I don't find it very diplomatic to use language like "Deallocation scheme for memoryview is complex and unsafe. It
crashes with chained memoryviews..." when you don't seem to
a test case or a clear concept of how the alleged bug should
occur.
msg253865 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2015-11-01 18:42
Sorry for my bad wording. I wasn't going to blame or insult anybody.
msg253925 - (view) Author: Stefan Krah (skrah) * (Python committer) Date: 2015-11-02 15:26
Thanks, no big problem.  The thing is that the parts I wrote
(Modules/_decimal/*, Objects/memoryobject.c, Modules/_testbuffer.c)
have been audited rather heavily and have 100% code coverage
with a privately maintained patch that inserts allocation
failures.

There are even tests in test_memoryview() / test_buffer() for
breaking up reference cycles.

So I'd really prefer not to have things labeled as potential issues
when there are none. That also applies to the PyMem_NEW() "potential
overflow" fix in _testbuffer.c where overflow was impossible.

Thus, I'll probably revert that at some point, not out of
hostility, but just because it's not my approach to software
development (in the _testbuffer case, I view the 'len' parameter
as a constrained type in the Ada sense where 0 <= len <= ndim = 64).
History
Date User Action Args
2022-04-11 14:58:23adminsetgithub: 69711
2015-11-02 15:26:51skrahsetmessages: + msg253925
2015-11-01 18:42:40serhiy.storchakasetmessages: + msg253865
2015-11-01 17:29:59skrahsetstatus: open -> closed
resolution: not a bug
messages: + msg253861

stage: patch review -> resolved
2015-11-01 14:00:10serhiy.storchakasetmessages: + msg253843
2015-11-01 13:56:25serhiy.storchakasetfiles: + memoryview_safer_clearing.patch
keywords: + patch
2015-11-01 10:16:39skrahsetmessages: + msg253839
2015-11-01 06:07:55serhiy.storchakasetmessages: + msg253826
2015-11-01 05:56:06serhiy.storchakasetmessages: + msg253825
2015-10-31 21:59:03serhiy.storchakasetmessages: + msg253812
2015-10-31 20:47:35martin.pantersetnosy: + martin.panter
messages: + msg253807
2015-10-31 20:40:31skrahsetmessages: + msg253806
2015-10-31 19:14:22serhiy.storchakacreate