diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index ad5893e6754f68c..e285c4bddd0b342 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -3072,6 +3072,22 @@ def test_new_prescr(self): del screen gc_collect() + def test_initscr_after_newterm_keeps_screen_alive(self): + # initscr() called while a newterm() screen is current returns a window + # for that screen, and the window must keep the screen alive. It used + # to be created without a screen, and using the window after the + # screen was collected read freed memory. + s1 = self.make_pty() + s2 = self.make_pty() + screen1 = curses.newterm('xterm', s1, s1) + screen2 = curses.newterm('xterm', s2, s2) + curses.set_term(screen1) + win = curses.initscr() + curses.set_term(screen2) + del screen1 + gc_collect() + win.addstr(0, 0, 'x') + @cpython_only def test_disallow_instantiation(self): # The screen type cannot be instantiated directly (bpo-43916). diff --git a/Misc/NEWS.d/next/Library/2026-07-26-21-11-58.gh-issue-154751.q-YeEM.rst b/Misc/NEWS.d/next/Library/2026-07-26-21-11-58.gh-issue-154751.q-YeEM.rst new file mode 100644 index 000000000000000..354447ca05d2bbe --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-07-26-21-11-58.gh-issue-154751.q-YeEM.rst @@ -0,0 +1,3 @@ +Fix a use-after-free in :mod:`curses`. :func:`curses.initscr` called while a +screen from :func:`curses.newterm` is current now returns a window that keeps +that screen alive, like :func:`curses.newwin` and :func:`curses.newpad` do. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index b2d745332317a36..aa06666a8201bbb 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -6573,7 +6573,11 @@ _curses_initscr_impl(PyObject *module) _curses_set_null_error(state, "wrefresh", "initscr"); return NULL; } - PyObject *winobj = PyCursesWindow_New(state, stdscr, NULL, NULL, NULL); + /* Attach the current screen, like newwin(), newpad() and getwin() do, + so that the window keeps its screen alive. It is NULL for the + screen created by initscr(), which has no screen object. */ + PyObject *winobj = PyCursesWindow_New(state, stdscr, NULL, NULL, + state->topscreen); if (winobj == NULL) { return NULL; }