winsch() recurses without bound when wcwidth() returns -1
Serhiy Storchaka <[email protected]> Thu, 30 Jul 2026 19:19:10 +0300
| Newsgroups | gmane.comp.lib.ncurses.bugs |
|---|---|
| Message-ID | <[email protected]> |
ncurses/widechar/lib_ins_wch.c:
int cells = _nc_wacs_width(CharOf(CHDEREF(wch)));
if (cells < 0) {
code = winsch(win, (chtype) CharOf(CHDEREF(wch)));
} else {
winsch() calls _nc_insert_ch(), which passes a non-control character
to its wide branch, where _nc_build_wch() builds the same character
and gives it back to _nc_insert_wch(), which finds the same negative
width:
frame #0: _nc_build_wch + 197
frame #1: _nc_insert_ch + 395
frame #2: winsch + 41
frame #3: _nc_insert_ch + 415
frame #4: winsch + 41
...
To repeat:
/* cc winsch_recurse.c -lncursesw -o winsch_recurse
LC_ALL=en_US.ISO8859-15 TERM=xterm ./winsch_recurse 0xA4 */
#include <curses.h>
#include <locale.h>
#include <stdio.h>
int main(void)
{
FILE *f = fopen("/dev/null", "w+");
setlocale(LC_ALL, "");
newterm("xterm", f, f);
winsch(stdscr, (chtype) 0xA4);
fprintf(stderr, "ok\n");
endwin();
return 0;
}
It takes a byte that is not a control character and whose wcwidth() is
negative. FreeBSD 15.1 has 57 of them in en_US.ISO8859-15 and 54 in
uk_UA.KOI8-U, because there wchar_t holds the locale value rather than
a code point; each crashes with the system libncursesw.so.9
(6.6.20251230) and with the 2026-07-19 snapshot. On glibc such a byte
does not seem to exist, and the same snapshot does not crash in any
locale I tried. UTF-8 locales are unaffected on both.
Found through Python's curses module, whose test suite calls insstr().
A character that the locale cannot represent has no width to insert,
so returning ERR ends the recursion:
--- a/ncurses/widechar/lib_ins_wch.c
+++ b/ncurses/widechar/lib_ins_wch.c
@@
if (cells < 0) {
- code = winsch(win, (chtype) CharOf(CHDEREF(wch)));
+ code = ERR;
} else {
With that the reproducer returns for every byte and locale I tried,
and a byte with a positive width is unaffected. I do not know whether
ERR is the right return there, or whether such a character should be
treated as one cell.