gh-120378: Fix crash caused by integer overflow in `curses` (#124555)

This is actually an upstream problem in curses, and has been reported
to them already:
https://lists.gnu.org/archive/html/bug-ncurses/2024-09/msg00101.html

This is a nice workaround in the meantime to prevent the segfault.

Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
Peter Bierma 2024-10-02 10:31:23 -04:00 committed by GitHub
parent 7bd9dbf8e1
commit c2ba931318
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 105 additions and 27 deletions

View File

@ -1081,6 +1081,14 @@ class TestCurses(unittest.TestCase):
self.assertEqual(curses.LINES, lines) self.assertEqual(curses.LINES, lines)
self.assertEqual(curses.COLS, cols) self.assertEqual(curses.COLS, cols)
with self.assertRaises(OverflowError):
curses.resize_term(35000, 1)
with self.assertRaises(OverflowError):
curses.resize_term(1, 35000)
# GH-120378: Overflow failure in resize_term() causes refresh to fail
tmp = curses.initscr()
tmp.erase()
@requires_curses_func('resizeterm') @requires_curses_func('resizeterm')
def test_resizeterm(self): def test_resizeterm(self):
curses.update_lines_cols() curses.update_lines_cols()
@ -1095,6 +1103,14 @@ class TestCurses(unittest.TestCase):
self.assertEqual(curses.LINES, lines) self.assertEqual(curses.LINES, lines)
self.assertEqual(curses.COLS, cols) self.assertEqual(curses.COLS, cols)
with self.assertRaises(OverflowError):
curses.resizeterm(35000, 1)
with self.assertRaises(OverflowError):
curses.resizeterm(1, 35000)
# GH-120378: Overflow failure in resizeterm() causes refresh to fail
tmp = curses.initscr()
tmp.erase()
def test_ungetch(self): def test_ungetch(self):
curses.ungetch(b'A') curses.ungetch(b'A')
self.assertEqual(self.stdscr.getkey(), 'A') self.assertEqual(self.stdscr.getkey(), 'A')

View File

@ -0,0 +1,2 @@
Fix a crash related to an integer overflow in :func:`curses.resizeterm`
and :func:`curses.resize_term`.

View File

@ -4204,9 +4204,9 @@ NoArgNoReturnFunctionBody(resetty)
/*[clinic input] /*[clinic input]
_curses.resizeterm _curses.resizeterm
nlines: int nlines: short
Height. Height.
ncols: int ncols: short
Width. Width.
/ /
@ -4217,8 +4217,8 @@ window dimensions (in particular the SIGWINCH handler).
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static PyObject *
_curses_resizeterm_impl(PyObject *module, int nlines, int ncols) _curses_resizeterm_impl(PyObject *module, short nlines, short ncols)
/*[clinic end generated code: output=56d6bcc5194ad055 input=0fca02ebad5ffa82]*/ /*[clinic end generated code: output=4de3abab50c67f02 input=414e92a63e3e9899]*/
{ {
PyObject *result; PyObject *result;
@ -4240,9 +4240,9 @@ _curses_resizeterm_impl(PyObject *module, int nlines, int ncols)
/*[clinic input] /*[clinic input]
_curses.resize_term _curses.resize_term
nlines: int nlines: short
Height. Height.
ncols: int ncols: short
Width. Width.
/ /
@ -4256,8 +4256,8 @@ without additional interaction with the application.
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static PyObject *
_curses_resize_term_impl(PyObject *module, int nlines, int ncols) _curses_resize_term_impl(PyObject *module, short nlines, short ncols)
/*[clinic end generated code: output=9e26d8b9ea311ed2 input=2197edd05b049ed4]*/ /*[clinic end generated code: output=46c6d749fa291dbd input=276afa43d8ea7091]*/
{ {
PyObject *result; PyObject *result;

View File

@ -3693,26 +3693,56 @@ PyDoc_STRVAR(_curses_resizeterm__doc__,
{"resizeterm", _PyCFunction_CAST(_curses_resizeterm), METH_FASTCALL, _curses_resizeterm__doc__}, {"resizeterm", _PyCFunction_CAST(_curses_resizeterm), METH_FASTCALL, _curses_resizeterm__doc__},
static PyObject * static PyObject *
_curses_resizeterm_impl(PyObject *module, int nlines, int ncols); _curses_resizeterm_impl(PyObject *module, short nlines, short ncols);
static PyObject * static PyObject *
_curses_resizeterm(PyObject *module, PyObject *const *args, Py_ssize_t nargs) _curses_resizeterm(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
int nlines; short nlines;
int ncols; short ncols;
if (!_PyArg_CheckPositional("resizeterm", nargs, 2, 2)) { if (!_PyArg_CheckPositional("resizeterm", nargs, 2, 2)) {
goto exit; goto exit;
} }
nlines = PyLong_AsInt(args[0]); {
if (nlines == -1 && PyErr_Occurred()) { long ival = PyLong_AsLong(args[0]);
if (ival == -1 && PyErr_Occurred()) {
goto exit; goto exit;
} }
ncols = PyLong_AsInt(args[1]); else if (ival < SHRT_MIN) {
if (ncols == -1 && PyErr_Occurred()) { PyErr_SetString(PyExc_OverflowError,
"signed short integer is less than minimum");
goto exit; goto exit;
} }
else if (ival > SHRT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"signed short integer is greater than maximum");
goto exit;
}
else {
nlines = (short) ival;
}
}
{
long ival = PyLong_AsLong(args[1]);
if (ival == -1 && PyErr_Occurred()) {
goto exit;
}
else if (ival < SHRT_MIN) {
PyErr_SetString(PyExc_OverflowError,
"signed short integer is less than minimum");
goto exit;
}
else if (ival > SHRT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"signed short integer is greater than maximum");
goto exit;
}
else {
ncols = (short) ival;
}
}
return_value = _curses_resizeterm_impl(module, nlines, ncols); return_value = _curses_resizeterm_impl(module, nlines, ncols);
exit: exit:
@ -3744,26 +3774,56 @@ PyDoc_STRVAR(_curses_resize_term__doc__,
{"resize_term", _PyCFunction_CAST(_curses_resize_term), METH_FASTCALL, _curses_resize_term__doc__}, {"resize_term", _PyCFunction_CAST(_curses_resize_term), METH_FASTCALL, _curses_resize_term__doc__},
static PyObject * static PyObject *
_curses_resize_term_impl(PyObject *module, int nlines, int ncols); _curses_resize_term_impl(PyObject *module, short nlines, short ncols);
static PyObject * static PyObject *
_curses_resize_term(PyObject *module, PyObject *const *args, Py_ssize_t nargs) _curses_resize_term(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{ {
PyObject *return_value = NULL; PyObject *return_value = NULL;
int nlines; short nlines;
int ncols; short ncols;
if (!_PyArg_CheckPositional("resize_term", nargs, 2, 2)) { if (!_PyArg_CheckPositional("resize_term", nargs, 2, 2)) {
goto exit; goto exit;
} }
nlines = PyLong_AsInt(args[0]); {
if (nlines == -1 && PyErr_Occurred()) { long ival = PyLong_AsLong(args[0]);
if (ival == -1 && PyErr_Occurred()) {
goto exit; goto exit;
} }
ncols = PyLong_AsInt(args[1]); else if (ival < SHRT_MIN) {
if (ncols == -1 && PyErr_Occurred()) { PyErr_SetString(PyExc_OverflowError,
"signed short integer is less than minimum");
goto exit; goto exit;
} }
else if (ival > SHRT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"signed short integer is greater than maximum");
goto exit;
}
else {
nlines = (short) ival;
}
}
{
long ival = PyLong_AsLong(args[1]);
if (ival == -1 && PyErr_Occurred()) {
goto exit;
}
else if (ival < SHRT_MIN) {
PyErr_SetString(PyExc_OverflowError,
"signed short integer is less than minimum");
goto exit;
}
else if (ival > SHRT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"signed short integer is greater than maximum");
goto exit;
}
else {
ncols = (short) ival;
}
}
return_value = _curses_resize_term_impl(module, nlines, ncols); return_value = _curses_resize_term_impl(module, nlines, ncols);
exit: exit:
@ -4318,4 +4378,4 @@ _curses_has_extended_color_support(PyObject *module, PyObject *Py_UNUSED(ignored
#ifndef _CURSES_USE_DEFAULT_COLORS_METHODDEF #ifndef _CURSES_USE_DEFAULT_COLORS_METHODDEF
#define _CURSES_USE_DEFAULT_COLORS_METHODDEF #define _CURSES_USE_DEFAULT_COLORS_METHODDEF
#endif /* !defined(_CURSES_USE_DEFAULT_COLORS_METHODDEF) */ #endif /* !defined(_CURSES_USE_DEFAULT_COLORS_METHODDEF) */
/*[clinic end generated code: output=96887782374f070a input=a9049054013a1b77]*/ /*[clinic end generated code: output=8745c1562b537fb4 input=a9049054013a1b77]*/