-
-
Notifications
You must be signed in to change notification settings - Fork 31.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Calling Py_DECREF
twice does not result in error with debug build
#109496
Comments
What happens if you print the refcount before and after each decref? |
I think that snippet about What happens is that the first Py_DECREF correctly decreases the ref count to 0, and deallocates the object. Now There might be an interesting bit here thanks to the new immortality check. There is a chance that, on debug builds, dereferencing garbage might look like an object with the immortality bit set, and Py_DECREF will just ignore it. Before the immortality check was there, it would probably have crashed or reported the error in one way or another, I guess? |
decref subtracts 1 and only ends at 0 if the refcount really started at 1. If it somehow was 2, 2 decrefs are not a bug. I know this seems impossible but so seems the observed behavior. Hence the debug suggestion to confirm the presumption. |
It is an interesting example in which the debug build hides a bug which can be exposed in the release build. In the debug build the deallocated memory if filled with some recognizable pattern. Therefore, bytes which contained the refcount no longer zero, and the second @vstinner, it may be interesting to you. It is not Python fault. Just does not do this. You should only call |
Py_DECREF() now calls _Py_NegativeRefcount() if the object is a dangling pointer to deallocated memory (filled with 0xDD "dead byte" by the debug hook in memory allocators). Check the reference count *before* checking for _Py_IsImmortal(). Add test_decref_freed_object() to test_capi.test_misc.
Py_DECREF() now calls _Py_NegativeRefcount() if the object is a dangling pointer to deallocated memory (filled with 0xDD "dead byte" by the debug hook in memory allocators). Check the reference count *before* checking for _Py_IsImmortal(). Add test_decref_freed_object() to test_capi.test_misc.
On a Python built in debug mode, Py_DECREF() now calls _Py_NegativeRefcount() if the object is a dangling pointer to deallocated memory: memory filled with 0xDD "dead byte" by the debug hook on memory allocators. The fix is to check the reference count *before* checking for _Py_IsImmortal(). Add test_decref_freed_object() to test_capi.test_misc.
I wrote PR #109539 to fix this Python 3.12 regression caused by immortal objects (PEP 683). When an object reference count if 1, Py_DECREF() calls _Py_Dealloc(). For example, for a Python str object, _Py_Dealloc() calls PyObject_Free(). Using PYTHONMALLOC=debug or a debug build of Python, PyObject_Free() calls At the end, the first Py_DECREF(obj) creates a dangling pointer: using obj is now undefined. But well, in most cases, dereferencing the pointer will not crash since Python rarely really gives the memory back to the system (kernel), but keeps it for performance. And so no SIGSEGV or SIGBUS is sent to Python. For example, pymalloc (used for memory blocks up to 512 bytes) only gives an arena of 256 KiB back to the system (kernel) once all* memory of this arena is freed... and memory fragmentation makes this event "rare". In short, the first if (_Py_IsImmortal(op)) {
return;
} And and and... this reference count is considered as immortal on x86-64 (64-bit
There are different options to trigger an error on the second Py_DECREF():
My PR checks |
On a Python built in debug mode, Py_DECREF() now calls _Py_NegativeRefcount() if the object is a dangling pointer to deallocated memory: memory filled with 0xDD "dead byte" by the debug hook on memory allocators. The fix is to check the reference count *before* checking for _Py_IsImmortal(). Add test_decref_freed_object() to test_capi.test_misc.
On a Python built in debug mode, Py_DECREF() now calls _Py_NegativeRefcount() if the object is a dangling pointer to deallocated memory: memory filled with 0xDD "dead byte" by the debug hook on memory allocators. The fix is to check the reference count *before* checking for _Py_IsImmortal(). Add test_decref_freed_object() to test_capi.test_misc.
…onGH-109539) On a Python built in debug mode, Py_DECREF() now calls _Py_NegativeRefcount() if the object is a dangling pointer to deallocated memory: memory filled with 0xDD "dead byte" by the debug hook on memory allocators. The fix is to check the reference count *before* checking for _Py_IsImmortal(). Add test_decref_freed_object() to test_capi.test_misc. (cherry picked from commit 0bb0d88) Co-authored-by: Victor Stinner <[email protected]>
…109539) (#109545) gh-109496: Detect Py_DECREF() after dealloc in debug mode (GH-109539) On a Python built in debug mode, Py_DECREF() now calls _Py_NegativeRefcount() if the object is a dangling pointer to deallocated memory: memory filled with 0xDD "dead byte" by the debug hook on memory allocators. The fix is to check the reference count *before* checking for _Py_IsImmortal(). Add test_decref_freed_object() to test_capi.test_misc. (cherry picked from commit 0bb0d88) Co-authored-by: Victor Stinner <[email protected]>
@vstinner thank you for the interesting explanation and quick fix! Confirmed using commit 0bb0d88 running the original example produces an expected error:
Closing issue. |
Ah, and thanks for your bug report! Apparently, nobody noticed the issue before! |
Skip test_decref_freed_object() of test_capi.test_misc if Python is built with ASAN, MSAN or UBSAN sanitizers.
Skip test_decref_freed_object() of test_capi.test_misc if Python is built with ASAN, MSAN or UBSAN sanitizers.
Skip test_decref_freed_object() of test_capi.test_misc if Python is built with ASAN, MSAN or UBSAN sanitizers.
…ythonGH-109573) Skip test_decref_freed_object() of test_capi.test_misc if Python is built with ASAN, MSAN or UBSAN sanitizers. (cherry picked from commit 0a31ff0) Co-authored-by: Victor Stinner <[email protected]>
…on#109539) On a Python built in debug mode, Py_DECREF() now calls _Py_NegativeRefcount() if the object is a dangling pointer to deallocated memory: memory filled with 0xDD "dead byte" by the debug hook on memory allocators. The fix is to check the reference count *before* checking for _Py_IsImmortal(). Add test_decref_freed_object() to test_capi.test_misc.
…ython#109573) Skip test_decref_freed_object() of test_capi.test_misc if Python is built with ASAN, MSAN or UBSAN sanitizers.
…H-109573) (#109578) Co-authored-by: Victor Stinner <[email protected]>
Bug report
Bug description:
Documented behavior of Py_REF_DEBUG includes:
Calling
Py_DECREF
twice does not result in the expected error:To reproduce:
Build CPython with
--with-pydebug
:Build this sample extension calling
Py_DECREF
twice using the debug build of CPython:PYTHON=/home/kevin/code/cpython/debug/python $PYTHON setup.py build
Run a test with this extension:
No error is indicated, but an error is expected.
Extension source is located here: https://github.com/kevinAlbs/double_decref_extension
Tested with cpython main branch on commit: 929cc4e.
If this issue is confirmed, I may be interested to investigate possible solutions.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux
Linked PRs
The text was updated successfully, but these errors were encountered: