Skip to content

gh-154749: Reject a terminal-less screen in curses.set_term() - #154750

Merged
serhiy-storchaka merged 4 commits into
python:mainfrom
fedonman:fix-curses-set-term-prescr
Jul 27, 2026
Merged

gh-154749: Reject a terminal-less screen in curses.set_term()#154750
serhiy-storchaka merged 4 commits into
python:mainfrom
fedonman:fix-curses-set-term-prescr

Conversation

@fedonman

@fedonman fedonman commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

curses.set_term() only checked that its argument is a screen that has not been
deleted, so it also accepted a screen from curses.new_prescr(). That screen has no
terminal attached, and once it was made current the next window refresh dereferenced
NULL inside curses and killed the interpreter:

import curses, os
fd = os.openpty()[1]
scr = curses.newterm('xterm', fd, fd)
curses.set_term(curses.new_prescr())
scr.stdscr.refresh()          # Segmentation fault, rc=139

set_term() now raises curses.error when the screen has no standard window, which is
exactly what a new_prescr() screen is. The documentation already described the argument
as a screen returned by newterm(), so this makes the code match, and the docs now
mention the exception.

Both functions are new in 3.16 (gh-90092, GH-151748), so no released version is affected
and there is no NEWS entry.

The same reproducer after the change:

set_term(new_prescr()) -> error: the screen has no terminal
refresh after the rejected switch: ok
set_term(real screen) still works, prev is a screen: True
refresh on the new current screen: ok

Tests:

$ ./python -m test -u all -v test_curses -m 'test_set_term_prescr_screen'
test_set_term_prescr_screen (test.test_curses.ScreenTests.test_set_term_prescr_screen) ... ok
OK

$ ./python -m test -u all test_curses
Total tests: run=164 skipped=3
Result: SUCCESS

curses.set_term() accepted a screen returned by new_prescr(), which has no
terminal attached. Making such a screen current left curses without a
terminal, and the next window refresh dereferenced NULL and crashed the
interpreter.

set_term() now raises curses.error for a screen with no standard window,
which is exactly a new_prescr() screen. The documentation already said the
argument comes from newterm().
@read-the-docs-community

read-the-docs-community Bot commented Jul 26, 2026

Copy link
Copy Markdown

Documentation build overview

📚 cpython-previews | 🛠️ Build #33783780 | 📁 Comparing df7e79a against main (5afbb60)

  🔍 Preview build  

3 files changed
± library/curses.html
± whatsnew/3.15.html
± whatsnew/changelog.html

@serhiy-storchaka
serhiy-storchaka self-requested a review July 27, 2026 12:35
Comment thread Lib/test/test_curses.py Outdated
Comment thread Misc/NEWS.d/next/Library/2026-07-26-20-22-45.gh-issue-154749.6ekT3T.rst Outdated
fedonman added 2 commits July 27, 2026 20:01
Both from review: the test uses the existing requires_curses_func helper, and
the NEWS entry goes away because set_term() and new_prescr() are both new in
3.16 and unreleased, so the crash was never reachable by a user.

@serhiy-storchaka serhiy-storchaka left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. 👍

@serhiy-storchaka
serhiy-storchaka enabled auto-merge (squash) July 27, 2026 18:26
@serhiy-storchaka
serhiy-storchaka merged commit b099df5 into python:main Jul 27, 2026
53 checks passed
@fedonman
fedonman deleted the fix-curses-set-term-prescr branch July 27, 2026 19:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants