Bug report
The module-global curses_screen_encoding in Modules/_cursesmodule.c stores a borrowed pointer to the encoding string owned by the window object that initscr() returns:
curses_screen_encoding = ((PyCursesWindowObject *)winobj)->encoding;
It is set only on the first initscr() call and is never updated afterwards. That first window object can be deallocated while the pointer is still needed: the module-level functions unctrl() and ungetch() have no window of their own and fall back to curses_screen_encoding to encode non-ASCII characters. Once the originating window is gone, those functions read freed memory.
It can be reached from Python by dropping the initial screen and then encoding a non-ASCII character, e.g.:
import curses, gc
w = curses.initscr()
try:
del w
gc.collect()
curses.initscr()
curses.ungetch('é') # reads the freed encoding string
finally:
curses.endwin()
The fix is to keep a private copy of the encoding instead of borrowing the window's, refreshed on every initscr() and released when the module is torn down.
Linked PRs
Bug report
The module-global
curses_screen_encodinginModules/_cursesmodule.cstores a borrowed pointer to theencodingstring owned by the window object thatinitscr()returns:It is set only on the first
initscr()call and is never updated afterwards. That first window object can be deallocated while the pointer is still needed: the module-level functionsunctrl()andungetch()have no window of their own and fall back tocurses_screen_encodingto encode non-ASCII characters. Once the originating window is gone, those functions read freed memory.It can be reached from Python by dropping the initial screen and then encoding a non-ASCII character, e.g.:
The fix is to keep a private copy of the encoding instead of borrowing the window's, refreshed on every
initscr()and released when the module is torn down.Linked PRs