-
-
Notifications
You must be signed in to change notification settings - Fork 31.1k
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
gh-122527: Fix a crash on deallocation of PyStructSequence
#122577
Conversation
The `PyStructSequence` destructor would crash if it was deallocated after its type's dictionary was cleared by the GC, because it couldn't compute the "real size" of the instance. This could occur with relatively straightforward code in the free-threaded build or with a reference cycle involving the type in the default build, due to differing orders in which `tp_clear()` was called. Account for the non-sequence fields in `tp_basicsize` and use that, along with `Py_SIZE()`, to compute the "real" size of a `PyStructSequence` in the dealloc function. This avoids the accesses to the type's dictionary during dealloc, which were unsafe.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great, thank you for the fix!
I think we should probably backport this to 3.13. And maybe 3.12? What do you think? |
I'll add the labels to get the PRs setup and CI started. If the decision ends up not to backport, we can just close those PRs. |
Thanks @colesbury for the PR, and @encukou for merging it 🌮🎉.. I'm working now to backport this PR to: 3.12. |
Thanks @colesbury for the PR, and @encukou for merging it 🌮🎉.. I'm working now to backport this PR to: 3.13. |
Sorry, @colesbury and @encukou, I could not cleanly backport this to
|
…ythonGH-122577) The `PyStructSequence` destructor would crash if it was deallocated after its type's dictionary was cleared by the GC, because it couldn't compute the "real size" of the instance. This could occur with relatively straightforward code in the free-threaded build or with a reference cycle involving the type in the default build, due to differing orders in which `tp_clear()` was called. Account for the non-sequence fields in `tp_basicsize` and use that, along with `Py_SIZE()`, to compute the "real" size of a `PyStructSequence` in the dealloc function. This avoids the accesses to the type's dictionary during dealloc, which were unsafe. (cherry picked from commit 4b63cd1) Co-authored-by: Sam Gross <[email protected]>
GH-122625 is a backport of this pull request to the 3.13 branch. |
GH-122626 is a backport of this pull request to the 3.12 branch. |
For 3.13.1, definitely. 3.12, doesn't have free-threading, and skipping a version when backporting is confusing, so I'd backport it after 3.13.1. Does that sound reasonable? |
…ythonGH-122577) The `PyStructSequence` destructor would crash if it was deallocated after its type's dictionary was cleared by the GC, because it couldn't compute the "real size" of the instance. This could occur with relatively straightforward code in the free-threaded build or with a reference cycle involving the type in the default build, due to differing orders in which `tp_clear()` was called. Account for the non-sequence fields in `tp_basicsize` and use that, along with `Py_SIZE()`, to compute the "real" size of a `PyStructSequence` in the dealloc function. This avoids the accesses to the type's dictionary during dealloc, which were unsafe.
@@ -41,12 +41,20 @@ get_type_attr_as_size(PyTypeObject *tp, PyObject *name) | |||
get_type_attr_as_size(tp, &_Py_ID(n_sequence_fields)) | |||
#define REAL_SIZE_TP(tp) \ | |||
get_type_attr_as_size(tp, &_Py_ID(n_fields)) | |||
#define REAL_SIZE(op) REAL_SIZE_TP(Py_TYPE(op)) | |||
#define REAL_SIZE(op) get_real_size((PyObject *)op) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not change also REAL_SIZE_TP
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new calculation involves Py_SIZE(op)
, so we can't do it on the type alone.
script_helper.assert_python_ok("-c", textwrap.dedent(r""" | ||
import time | ||
t = time.gmtime() | ||
type(t).refcyle = t |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: refcyle
.
…ythonGH-122577) The `PyStructSequence` destructor would crash if it was deallocated after its type's dictionary was cleared by the GC, because it couldn't compute the "real size" of the instance. This could occur with relatively straightforward code in the free-threaded build or with a reference cycle involving the type in the default build, due to differing orders in which `tp_clear()` was called. Account for the non-sequence fields in `tp_basicsize` and use that, along with `Py_SIZE()`, to compute the "real" size of a `PyStructSequence` in the dealloc function. This avoids the accesses to the type's dictionary during dealloc, which were unsafe.
…H-122577) (#122625) gh-122527: Fix a crash on deallocation of `PyStructSequence` (GH-122577) The `PyStructSequence` destructor would crash if it was deallocated after its type's dictionary was cleared by the GC, because it couldn't compute the "real size" of the instance. This could occur with relatively straightforward code in the free-threaded build or with a reference cycle involving the type in the default build, due to differing orders in which `tp_clear()` was called. Account for the non-sequence fields in `tp_basicsize` and use that, along with `Py_SIZE()`, to compute the "real" size of a `PyStructSequence` in the dealloc function. This avoids the accesses to the type's dictionary during dealloc, which were unsafe. (cherry picked from commit 4b63cd1) Co-authored-by: Sam Gross <[email protected]>
…H-122577) (#122626) The `PyStructSequence` destructor would crash if it was deallocated after its type's dictionary was cleared by the GC, because it couldn't compute the "real size" of the instance. This could occur with relatively straightforward code in the free-threaded build or with a reference cycle involving the type in the default build, due to differing orders in which `tp_clear()` was called. Account for the non-sequence fields in `tp_basicsize` and use that, along with `Py_SIZE()`, to compute the "real" size of a `PyStructSequence` in the dealloc function. This avoids the accesses to the type's dictionary during dealloc, which were unsafe. (cherry picked from commit 4b63cd1)
The
PyStructSequence
destructor would crash if it was deallocated after its type's dictionary was cleared by the GC, because it couldn't compute the "real size" of the instance. This could occur with relatively straightforward code in the free-threaded build or with a reference cycle involving the type in the default build, due to differing orders in whichtp_clear()
was called.Account for the non-sequence fields in
tp_basicsize
and use that, along withPy_SIZE()
, to compute the "real" size of aPyStructSequence
in the dealloc function. This avoids the accesses to the type's dictionary during dealloc, which were unsafe.