mirror of
https://github.com/python/cpython.git
synced 2026-01-04 14:32:21 +00:00
[3.13] bpo-44172: Keep reference to original window in curses subwindow objects (GH-26226) (GH-133370)
The X/Open curses specification[0] and ncurses documentation[1]
both state that subwindows must be deleted before the main window.
Deleting the windows in the wrong order causes a double-free with
NetBSD's curses implementation.
To fix this, keep track of the original window object in the subwindow
object, and keep a reference to the original for the lifetime of
the subwindow.
[0] https://pubs.opengroup.org/onlinepubs/7908799/xcurses/delwin.html
[1] https://invisible-island.net/ncurses/man/curs_window.3x.html
(cherry picked from commit 0af61fe2f4)
Co-authored-by: Michael Forney <mforney@mforney.org>
This commit is contained in:
parent
e090f8e3f5
commit
3c9d1778ef
4 changed files with 26 additions and 10 deletions
|
|
@ -75,10 +75,11 @@ extern "C" {
|
|||
|
||||
/* Type declarations */
|
||||
|
||||
typedef struct {
|
||||
typedef struct PyCursesWindowObject {
|
||||
PyObject_HEAD
|
||||
WINDOW *win;
|
||||
char *encoding;
|
||||
struct PyCursesWindowObject *orig;
|
||||
} PyCursesWindowObject;
|
||||
|
||||
#define PyCursesWindow_Check(v) Py_IS_TYPE((v), &PyCursesWindow_Type)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
from unittest.mock import MagicMock
|
||||
|
||||
from test.support import (requires, verbose, SaveSignals, cpython_only,
|
||||
check_disallow_instantiation, MISSING_C_DOCSTRINGS)
|
||||
check_disallow_instantiation, MISSING_C_DOCSTRINGS,
|
||||
gc_collect)
|
||||
from test.support.import_helper import import_module
|
||||
|
||||
# Optionally test curses module. This currently requires that the
|
||||
|
|
@ -187,6 +188,14 @@ def test_create_windows(self):
|
|||
self.assertEqual(win3.getparyx(), (2, 1))
|
||||
self.assertEqual(win3.getmaxyx(), (6, 11))
|
||||
|
||||
def test_subwindows_references(self):
|
||||
win = curses.newwin(5, 10)
|
||||
win2 = win.subwin(3, 7)
|
||||
del win
|
||||
gc_collect()
|
||||
del win2
|
||||
gc_collect()
|
||||
|
||||
def test_move_cursor(self):
|
||||
stdscr = self.stdscr
|
||||
win = stdscr.subwin(10, 15, 2, 5)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Keep a reference to original :mod:`curses` windows in subwindows so
|
||||
that the original window does not get deleted before subwindows.
|
||||
|
|
@ -666,7 +666,8 @@ Window_TwoArgNoReturnFunction(wresize, int, "ii;lines,columns")
|
|||
/* Allocation and deallocation of Window Objects */
|
||||
|
||||
static PyObject *
|
||||
PyCursesWindow_New(WINDOW *win, const char *encoding)
|
||||
PyCursesWindow_New(WINDOW *win, const char *encoding,
|
||||
PyCursesWindowObject *orig)
|
||||
{
|
||||
PyCursesWindowObject *wo;
|
||||
|
||||
|
|
@ -697,6 +698,8 @@ PyCursesWindow_New(WINDOW *win, const char *encoding)
|
|||
PyErr_NoMemory();
|
||||
return NULL;
|
||||
}
|
||||
wo->orig = orig;
|
||||
Py_XINCREF(orig);
|
||||
return (PyObject *)wo;
|
||||
}
|
||||
|
||||
|
|
@ -706,6 +709,7 @@ PyCursesWindow_Dealloc(PyCursesWindowObject *wo)
|
|||
if (wo->win != stdscr) delwin(wo->win);
|
||||
if (wo->encoding != NULL)
|
||||
PyMem_Free(wo->encoding);
|
||||
Py_XDECREF(wo->orig);
|
||||
PyObject_Free(wo);
|
||||
}
|
||||
|
||||
|
|
@ -1309,7 +1313,7 @@ _curses_window_derwin_impl(PyCursesWindowObject *self, int group_left_1,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return (PyObject *)PyCursesWindow_New(win, NULL);
|
||||
return (PyObject *)PyCursesWindow_New(win, NULL, self);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
|
@ -2336,7 +2340,7 @@ _curses_window_subwin_impl(PyCursesWindowObject *self, int group_left_1,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return (PyObject *)PyCursesWindow_New(win, self->encoding);
|
||||
return (PyObject *)PyCursesWindow_New(win, self->encoding, self);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
|
@ -3084,7 +3088,7 @@ _curses_getwin(PyObject *module, PyObject *file)
|
|||
PyErr_SetString(PyCursesError, catchall_NULL);
|
||||
goto error;
|
||||
}
|
||||
res = PyCursesWindow_New(win, NULL);
|
||||
res = PyCursesWindow_New(win, NULL, NULL);
|
||||
|
||||
error:
|
||||
fclose(fp);
|
||||
|
|
@ -3257,7 +3261,7 @@ _curses_initscr_impl(PyObject *module)
|
|||
|
||||
if (initialised) {
|
||||
wrefresh(stdscr);
|
||||
return (PyObject *)PyCursesWindow_New(stdscr, NULL);
|
||||
return (PyObject *)PyCursesWindow_New(stdscr, NULL, NULL);
|
||||
}
|
||||
|
||||
win = initscr();
|
||||
|
|
@ -3349,7 +3353,7 @@ _curses_initscr_impl(PyObject *module)
|
|||
SetDictInt("LINES", LINES);
|
||||
SetDictInt("COLS", COLS);
|
||||
|
||||
winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL);
|
||||
winobj = (PyCursesWindowObject *)PyCursesWindow_New(win, NULL, NULL);
|
||||
screen_encoding = winobj->encoding;
|
||||
return (PyObject *)winobj;
|
||||
}
|
||||
|
|
@ -3728,7 +3732,7 @@ _curses_newpad_impl(PyObject *module, int nlines, int ncols)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return (PyObject *)PyCursesWindow_New(win, NULL);
|
||||
return (PyObject *)PyCursesWindow_New(win, NULL, NULL);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
|
@ -3767,7 +3771,7 @@ _curses_newwin_impl(PyObject *module, int nlines, int ncols,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
return (PyObject *)PyCursesWindow_New(win, NULL);
|
||||
return (PyObject *)PyCursesWindow_New(win, NULL, NULL);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue