From 7ef0e3204040eeb13e6e6f0833cf91f2f78c2af5 Mon Sep 17 00:00:00 2001 From: Vyron Vasileiadis Date: Mon, 27 Jul 2026 22:03:38 +0300 Subject: [PATCH 1/3] gh-154781: Zero the in_wstr() buffer in curses winnwstr() only writes the terminating null when it stored at least one character, so with n of 0 the result was read from uninitialized memory. Use PyMem_Calloc, like in_wchstr() already does. --- Lib/test/test_curses.py | 6 ++++++ Modules/_cursesmodule.c | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_curses.py b/Lib/test/test_curses.py index 31b7371abd3230..82bb10ad58c635 100644 --- a/Lib/test/test_curses.py +++ b/Lib/test/test_curses.py @@ -514,6 +514,12 @@ def test_in_wstr(self): self.assertEqual(stdscr.in_wstr(0, 0, len(s)), s) self.assertIsInstance(stdscr.instr(0, 0, len(s)), bytes) + # Reading no characters gives an empty string, like instr() and + # in_wchstr() do. curses does not terminate the buffer in this case. + stdscr.addstr(0, 0, 'abz') + self.assertEqual(stdscr.in_wstr(0, 0, 0), '') + self.assertEqual(stdscr.in_wstr(0), '') + def test_complexchar(self): # A complexchar is a styled wide-character cell: str() is its text, # and the attr and pair attributes are its rendition. diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 2b580c3475e6d9..1ad7358dfa5e36 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -3850,7 +3850,9 @@ PyCursesWindow_in_wstr(PyObject *op, PyObject *args) } n = Py_MIN(n, max_buf_size - 1); - wchar_t *buf = PyMem_New(wchar_t, n + 1); + /* Zero the buffer: winnwstr() only writes the terminating null when it + stored at least one character, and the result is read up to it. */ + wchar_t *buf = PyMem_Calloc(n + 1, sizeof(wchar_t)); if (buf == NULL) { return PyErr_NoMemory(); } From 28d971da830cf8913317aef52cf672d3edd92d20 Mon Sep 17 00:00:00 2001 From: Vyron Vasileiadis Date: Mon, 27 Jul 2026 22:57:39 +0300 Subject: [PATCH 2/3] remove comment --- Modules/_cursesmodule.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 1ad7358dfa5e36..7d794b879e34ff 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -3850,8 +3850,6 @@ PyCursesWindow_in_wstr(PyObject *op, PyObject *args) } n = Py_MIN(n, max_buf_size - 1); - /* Zero the buffer: winnwstr() only writes the terminating null when it - stored at least one character, and the result is read up to it. */ wchar_t *buf = PyMem_Calloc(n + 1, sizeof(wchar_t)); if (buf == NULL) { return PyErr_NoMemory(); From 6c6b09478e8e947a7ee990c6b86283e776dfde7f Mon Sep 17 00:00:00 2001 From: Vyron Vasileiadis Date: Tue, 28 Jul 2026 23:47:15 +0300 Subject: [PATCH 3/3] apply reviewers recommendation --- Modules/_cursesmodule.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/_cursesmodule.c b/Modules/_cursesmodule.c index 7d794b879e34ff..8b75df5f9df70e 100644 --- a/Modules/_cursesmodule.c +++ b/Modules/_cursesmodule.c @@ -3850,7 +3850,7 @@ PyCursesWindow_in_wstr(PyObject *op, PyObject *args) } n = Py_MIN(n, max_buf_size - 1); - wchar_t *buf = PyMem_Calloc(n + 1, sizeof(wchar_t)); + wchar_t *buf = PyMem_New(wchar_t, n + 1); if (buf == NULL) { return PyErr_NoMemory(); } @@ -3866,7 +3866,7 @@ PyCursesWindow_in_wstr(PyObject *op, PyObject *args) PyMem_Free(buf); return Py_GetConstant(Py_CONSTANT_EMPTY_STR); } - PyObject *res = PyUnicode_FromWideChar(buf, -1); + PyObject *res = PyUnicode_FromWideChar(buf, rtn); PyMem_Free(buf); return res; #else