mirror of
https://github.com/python/cpython.git
synced 2026-10-28 10:25:05 +08:00
gh-156946: Unlink a curses panel before dropping its user pointer (GH-156947)
A __del__ of the user pointer could get the panel being deallocated from top_panel() and crash the interpreter.
This commit is contained in:
1 parent
1bfcc3cc87
commit
df02e264c8
3 files changed
+29
-9
No files matched your search
@@ -2431,6 +2431,22 @@ class TestCurses(unittest.TestCase):
|
||||
panel.set_userptr(A())
|
||||
panel.set_userptr(None)
|
||||
|
||||
@requires_curses_func('panel')
|
||||
def test_userptr_dealloc_segfault(self):
|
||||
w = curses.newwin(10, 10)
|
||||
panel = curses.panel.new_panel(w)
|
||||
seen = []
|
||||
class A:
|
||||
def __del__(self):
|
||||
# The panel is being deallocated, so it must already be off
|
||||
# the stack: handing it back here would resurrect an object
|
||||
# whose refcount is zero -- segfaults.
|
||||
seen.append(curses.panel.top_panel() is None)
|
||||
panel.set_userptr(A())
|
||||
del panel
|
||||
gc_collect()
|
||||
self.assertEqual(seen, [True])
|
||||
|
||||
@cpython_only
|
||||
@requires_curses_func('panel')
|
||||
def test_disallow_instantiation(self):
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
Fix a crash in :mod:`curses.panel` when the finalizer of a panel's user
|
||||
pointer runs while the panel is being deallocated. The panel is now taken
|
||||
off the panel stack before its user pointer is dropped.
|
||||
+10
-9
@@ -437,11 +437,11 @@ PyCursesPanel_Clear(PyObject *op)
|
||||
PyCursesPanelObject *self = _PyCursesPanelObject_CAST(op);
|
||||
PyObject *extra = (PyObject *)panel_userptr(self->pan);
|
||||
if (extra != NULL) {
|
||||
Py_DECREF(extra);
|
||||
if (set_panel_userptr(self->pan, NULL) == ERR) {
|
||||
curses_panel_panel_set_error(self, "set_panel_userptr", NULL);
|
||||
return -1;
|
||||
}
|
||||
Py_DECREF(extra);
|
||||
}
|
||||
// self->wo should not be cleared because an associated WINDOW may exist
|
||||
return 0;
|
||||
@@ -454,20 +454,21 @@ PyCursesPanel_Dealloc(PyObject *self)
|
||||
PyObject_GC_UnTrack(self);
|
||||
|
||||
PyCursesPanelObject *po = _PyCursesPanelObject_CAST(self);
|
||||
if (PyCursesPanel_Clear(self) < 0) {
|
||||
PyObject *extra = (PyObject *)panel_userptr(po->pan);
|
||||
if (extra != NULL && set_panel_userptr(po->pan, NULL) == ERR) {
|
||||
curses_panel_panel_set_error(po, "set_panel_userptr", "__del__");
|
||||
PyErr_FormatUnraisable("Exception ignored in PyCursesPanel_Dealloc()");
|
||||
}
|
||||
if (po->wo != NULL && remove_lop(po) < 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "__del__: no panel object to delete");
|
||||
PyErr_FormatUnraisable("Exception ignored in PyCursesPanel_Dealloc()");
|
||||
}
|
||||
if (del_panel(po->pan) == ERR && !PyErr_Occurred()) {
|
||||
curses_panel_panel_set_error(po, "del_panel", "__del__");
|
||||
PyErr_FormatUnraisable("Exception ignored in PyCursesPanel_Dealloc()");
|
||||
}
|
||||
if (po->wo != NULL) {
|
||||
Py_DECREF(po->wo);
|
||||
if (remove_lop(po) < 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "__del__: no panel object to delete");
|
||||
PyErr_FormatUnraisable("Exception ignored in PyCursesPanel_Dealloc()");
|
||||
}
|
||||
}
|
||||
Py_XDECREF(extra);
|
||||
Py_XDECREF(po->wo);
|
||||
tp->tp_free(po);
|
||||
Py_DECREF(tp);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user