window.in_wch(), window.in_wchstr() and window.getbkgrnd() can return a complexchar whose text is garbage, usually surfacing as ValueError: character U+xxxxxxxx is not in range [U+0000; U+10ffff], when curses is built against a curses library whose cell-read functions don't NUL-terminate the cchar_t text array.
The module reads a cell into an uninitialized cchar_t (e.g. win_wch(win, &wcval)) then extracts the text with getcchar(). This relies on two behaviours ncurses happens to provide but X/Open doesn't require: that the read leaves the text array NUL-terminated, and that getcchar() writes the output text for an empty cell. NetBSD curses does neither — its read functions assume the caller zeroed the struct, and its getcchar() finds the text by scanning for L'\0' and writes nothing for an empty cell — so uninitialized bytes get interpreted as wide characters.
This doesn't reproduce on a stock ncursesw build; it's a latent bug that manifests with other backends (found testing against NetBSD curses), but the assumption is worth not depending on.
Fix:
- Zero-initialize the cell buffers before the read:
in_wch/getbkgrnd (curses_cell_t wcval = {0};) and in_wchstr (PyMem_New → PyMem_Calloc).
- Default the
getcchar() output to an empty string in the curses_getcchar() wrapper (wstr[0] = L'\0';), since getcchar() need not write the text of an empty cell.
Both are no-ops on ncurses; test_curses keeps passing and stays refleak-clean.
Linked PRs
window.in_wch(),window.in_wchstr()andwindow.getbkgrnd()can return acomplexcharwhose text is garbage, usually surfacing asValueError: character U+xxxxxxxx is not in range [U+0000; U+10ffff], whencursesis built against a curses library whose cell-read functions don't NUL-terminate thecchar_ttext array.The module reads a cell into an uninitialized
cchar_t(e.g.win_wch(win, &wcval)) then extracts the text withgetcchar(). This relies on two behaviours ncurses happens to provide but X/Open doesn't require: that the read leaves the text array NUL-terminated, and thatgetcchar()writes the output text for an empty cell. NetBSD curses does neither — its read functions assume the caller zeroed the struct, and itsgetcchar()finds the text by scanning forL'\0'and writes nothing for an empty cell — so uninitialized bytes get interpreted as wide characters.This doesn't reproduce on a stock ncursesw build; it's a latent bug that manifests with other backends (found testing against NetBSD curses), but the assumption is worth not depending on.
Fix:
in_wch/getbkgrnd(curses_cell_t wcval = {0};) andin_wchstr(PyMem_New→PyMem_Calloc).getcchar()output to an empty string in thecurses_getcchar()wrapper (wstr[0] = L'\0';), sincegetcchar()need not write the text of an empty cell.Both are no-ops on ncurses;
test_curseskeeps passing and stays refleak-clean.Linked PRs