Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix a use-after-free in the :mod:`curses` module. The encoding of the initial
screen, used by :func:`curses.unctrl` and :func:`curses.ungetch` to encode
non-ASCII characters, is now kept as a private copy instead of a borrowed
pointer to a window object that may be deallocated.
38 changes: 35 additions & 3 deletions Modules/_cursesmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,11 @@ static int curses_initscr_called = FALSE;
/* Tells whether start_color() has been called to initialise color usage. */
static int curses_start_color_called = FALSE;

static const char *curses_screen_encoding = NULL;
/* Encoding of the initial screen, used by module-level functions that have
no window object to take it from (e.g. unctrl(), ungetch()). This is a
private copy: the window object that initscr() returns may be deallocated
while these functions are still in use. */
static char *curses_screen_encoding = NULL;

/* Utility Error Procedures */

Expand Down Expand Up @@ -3799,6 +3803,21 @@ _curses_init_pair_impl(PyObject *module, int pair_number, int fg, int bg)
Py_RETURN_NONE;
}

/* Refresh the private copy of the screen encoding from a freshly created
stdscr window object. Returns 0 on success, -1 with an exception set. */
static int
curses_update_screen_encoding(PyObject *winobj)
{
char *copy = _PyMem_Strdup(((PyCursesWindowObject *)winobj)->encoding);
if (copy == NULL) {
PyErr_NoMemory();
return -1;
}
PyMem_Free(curses_screen_encoding);
curses_screen_encoding = copy;
return 0;
}

/*[clinic input]
_curses.initscr

Expand All @@ -3820,7 +3839,15 @@ _curses_initscr_impl(PyObject *module)
_curses_set_null_error(state, "wrefresh", "initscr");
return NULL;
}
return PyCursesWindow_New(state, stdscr, NULL, NULL);
PyObject *winobj = PyCursesWindow_New(state, stdscr, NULL, NULL);
if (winobj == NULL) {
return NULL;
}
if (curses_update_screen_encoding(winobj) < 0) {
Py_DECREF(winobj);
return NULL;
}
return winobj;
}

win = initscr();
Expand Down Expand Up @@ -3927,7 +3954,10 @@ _curses_initscr_impl(PyObject *module)
if (winobj == NULL) {
return NULL;
}
curses_screen_encoding = ((PyCursesWindowObject *)winobj)->encoding;
if (curses_update_screen_encoding(winobj) < 0) {
Py_DECREF(winobj);
return NULL;
}
return winobj;
}

Expand Down Expand Up @@ -5480,6 +5510,8 @@ static void
cursesmodule_free(void *mod)
{
(void)cursesmodule_clear((PyObject *)mod);
PyMem_Free(curses_screen_encoding);
curses_screen_encoding = NULL;
curses_module_loaded = 0; // allow reloading once garbage-collected
}

Expand Down
Loading