https://github.com/python/cpython/commit/e7b21b66a4ddc0ed57f74972bfb7cf1b678fca0a
commit: e7b21b66a4ddc0ed57f74972bfb7cf1b678fca0a
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-06-29T12:06:47Z
summary:

gh-152503: Fix garbage text from curses wide-character cell reads (GH-152505)

window.in_wch(), window.in_wchstr() and window.getbkgrnd() read a cell
into an uninitialized cchar_t, relying on the curses library to leave the
text NUL-terminated -- which ncurses does but X/Open does not require, so
some libraries (such as NetBSD curses) returned uninitialized bytes as
wide characters.  Zero-initialize the cell buffers before the read and
default the getcchar() output to an empty string.

Co-authored-by: Claude Opus 4.8 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-06-28-16-20-07.gh-issue-152503.N55ose.rst
M Modules/_cursesmodule.c

diff --git 
a/Misc/NEWS.d/next/Library/2026-06-28-16-20-07.gh-issue-152503.N55ose.rst 
b/Misc/NEWS.d/next/Library/2026-06-28-16-20-07.gh-issue-152503.N55ose.rst
new file mode 100644
index 00000000000000..e97c5d74600c67
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-06-28-16-20-07.gh-issue-152503.N55ose.rst
@@ -0,0 +1,4 @@
+Fix :meth:`curses.window.in_wch`, :meth:`curses.window.in_wchstr` and
+:meth:`curses.window.getbkgrnd` returning garbage text when :mod:`curses` is
+built against a curses library that does not NUL-terminate the ``cchar_t``
+text array (such as NetBSD curses).
diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c
index 4e7b27cdb7be6e..07883848992a20 100644
--- a/Modules/_cursesmodule.c
+++ b/Modules/_cursesmodule.c
@@ -765,6 +765,9 @@ static int
 curses_getcchar(const cchar_t *wcval, wchar_t *wstr, attr_t *attrs, int *pair)
 {
     short spair = 0;
+    /* getcchar() is not guaranteed to write the text of an empty cell, so make
+       the output an empty string by default. */
+    wstr[0] = L'\0';
 #if _NCURSES_EXTENDED_COLOR_FUNCS
     int rtn = getcchar(wcval, wstr, attrs, &spair, pair);
 #else
@@ -3079,7 +3082,8 @@ _curses_window_in_wch_impl(PyCursesWindowObject *self, 
int group_right_1,
                            int y, int x)
 /*[clinic end generated code: output=846ca8a82f2ecab4 input=a55dd215367dfbb1]*/
 {
-    curses_cell_t wcval;
+    /* Zeroed so getcchar() sees a NUL-terminated text array on read. */
+    curses_cell_t wcval = {0};
     cursesmodule_state *state = get_cursesmodule_state_by_win(self);
 #ifdef HAVE_NCURSESW
     int rtn;
@@ -3126,7 +3130,8 @@ static PyObject *
 _curses_window_getbkgrnd_impl(PyCursesWindowObject *self)
 /*[clinic end generated code: output=afec19cad00eff71 input=e06bf3d6bf90d2ec]*/
 {
-    curses_cell_t wcval;
+    /* Zeroed so getcchar() sees a NUL-terminated text array on read. */
+    curses_cell_t wcval = {0};
     cursesmodule_state *state = get_cursesmodule_state_by_win(self);
 #ifdef HAVE_NCURSESW
     if (wgetbkgrnd(self->win, &wcval) == ERR) {
@@ -3842,7 +3847,10 @@ PyCursesWindow_in_wchstr(PyObject *op, PyObject *args)
 
     n = Py_MIN(n, max_buf_size - 1);
     cursesmodule_state *state = get_cursesmodule_state_by_win(self);
-    curses_cell_t *buf = PyMem_New(curses_cell_t, n + 1);
+    /* Zero the cells: reading a cell back through getcchar() relies on the
+       cchar_t text array being NUL-terminated, which some curses libraries
+       only guarantee for the characters they actually write. */
+    curses_cell_t *buf = PyMem_Calloc(n + 1, sizeof(curses_cell_t));
     if (buf == NULL) {
         return PyErr_NoMemory();
     }

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to