Skip to content

Commit b099df5

Browse files
authored
gh-154749: Reject a terminal-less screen in curses.set_term() (GH-154750)
set_term() accepted a screen returned by new_prescr(), which has no terminal, and the next refresh crashed inside curses. Raise curses.error instead.
1 parent 92efaff commit b099df5

3 files changed

Lines changed: 20 additions & 1 deletion

File tree

Doc/library/curses.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ Initialization and termination
129129
and return the previously current screen.
130130
Returns ``None`` if the previous screen was the one created by
131131
:func:`initscr`.
132+
Raises :exc:`error` if *screen* has no terminal,
133+
as is the case for a screen returned by :func:`new_prescr`.
132134

133135
.. versionadded:: next
134136

Lib/test/test_curses.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3072,6 +3072,17 @@ def test_new_prescr(self):
30723072
del screen
30733073
gc_collect()
30743074

3075+
@requires_curses_func('new_prescr')
3076+
def test_set_term_prescr_screen(self):
3077+
# A new_prescr() screen has no terminal, so it cannot become the
3078+
# current one. It used to be accepted, and the next refresh then
3079+
# crashed inside curses.
3080+
s = self.make_pty()
3081+
screen = curses.newterm('xterm', s, s)
3082+
self.assertRaises(curses.error, curses.set_term, curses.new_prescr())
3083+
# The current screen is unchanged, so refreshing it still works.
3084+
screen.stdscr.refresh()
3085+
30753086
def test_initscr_after_newterm_keeps_screen_alive(self):
30763087
# initscr() called while a newterm() screen is current returns that
30773088
# screen's own standard window, so the window keeps the screen alive.

Modules/_cursesmodule.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6850,11 +6850,17 @@ _curses_set_term(PyObject *module, PyObject *screen)
68506850
if (so == NULL) {
68516851
return NULL;
68526852
}
6853+
cursesmodule_state *state = get_cursesmodule_state(module);
6854+
if (so->stdscr_win == NULL) {
6855+
/* A screen from new_prescr() has no terminal, so it cannot become the
6856+
current one: a later refresh would dereference NULL in curses. */
6857+
PyErr_SetString(state->error, "the screen has no terminal");
6858+
return NULL;
6859+
}
68536860
set_term(so->screen);
68546861
if (!update_lines_cols(module)) {
68556862
return NULL;
68566863
}
6857-
cursesmodule_state *state = get_cursesmodule_state(module);
68586864
PyObject *prev = state->topscreen; /* steal the owned reference */
68596865
state->topscreen = Py_NewRef(screen);
68606866
return prev != NULL ? prev : Py_NewRef(Py_None);

0 commit comments

Comments
 (0)