Skip to content

Commit 04829d4

Browse files
miss-islingtonzydtigerblurb-it[bot]gpsheadpicnixz
authored
[3.14] gh-134209: use heap-allocated memory in _curses.window.{instr,getstr} (GH-134283) (#134391)
gh-134209: use heap-allocated memory in `_curses.window.{instr,getstr}` (GH-134283) * made curses buffer heap allocated instead of stack * change docs to explicitly mention the max buffer size * changing GetStr() function to behave similarly too * Update Doc/library/curses.rst * Update instr with proper return error handling * Update Modules/_cursesmodule.c * change to strlen and better memory safety * change from const int to Py_ssize_t * add mem allocation guard * update versionchanged to mention it was an increase. * explicitly use versionchanged 3.14 as that is its own branch now. TESTED: `python -m test -u curses test_curses` --------- (cherry picked from commit aadda87) Co-authored-by: tigerding <[email protected]> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Gregory P. Smith <[email protected]> Co-authored-by: Bénédikt Tran <[email protected]>
1 parent 379805d commit 04829d4

File tree

3 files changed

+80
-33
lines changed

3 files changed

+80
-33
lines changed

Doc/library/curses.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,10 @@ the following methods and attributes:
988988
window.getstr(y, x, n)
989989

990990
Read a bytes object from the user, with primitive line editing capacity.
991+
The maximum value for *n* is 2047.
992+
993+
.. versionchanged:: 3.14
994+
The maximum value for *n* was increased from 1023 to 2047.
991995

992996

993997
.. method:: window.getyx()
@@ -1079,6 +1083,10 @@ the following methods and attributes:
10791083
current cursor position, or at *y*, *x* if specified. Attributes are stripped
10801084
from the characters. If *n* is specified, :meth:`instr` returns a string
10811085
at most *n* characters long (exclusive of the trailing NUL).
1086+
The maximum value for *n* is 2047.
1087+
1088+
.. versionchanged:: 3.14
1089+
The maximum value for *n* was increased from 1023 to 2047.
10821090

10831091

10841092
.. method:: window.is_linetouched(line)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`curses`: The :meth:`curses.window.instr` and :meth:`curses.window.getstr`
2+
methods now allocate their internal buffer on the heap instead of the stack;
3+
in addition, the max buffer size is increased from 1023 to 2047.

Modules/_cursesmodule.c

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,7 +1687,7 @@ _curses.window.getstr
16871687
x: int
16881688
X-coordinate.
16891689
]
1690-
n: int = 1023
1690+
n: int = 2047
16911691
Maximal number of characters.
16921692
/
16931693
@@ -1700,62 +1700,80 @@ PyCursesWindow_GetStr(PyObject *op, PyObject *args)
17001700
PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op);
17011701

17021702
int x, y, n;
1703-
char rtn[1024]; /* This should be big enough.. I hope */
1704-
int rtn2;
1703+
int rtn;
1704+
1705+
/* could make the buffer size larger/dynamic */
1706+
Py_ssize_t max_buf_size = 2048;
1707+
PyObject *result = PyBytes_FromStringAndSize(NULL, max_buf_size);
1708+
if (result == NULL)
1709+
return NULL;
1710+
char *buf = PyBytes_AS_STRING(result);
17051711

17061712
switch (PyTuple_Size(args)) {
17071713
case 0:
17081714
Py_BEGIN_ALLOW_THREADS
1709-
rtn2 = wgetnstr(self->win,rtn, 1023);
1715+
rtn = wgetnstr(self->win, buf, max_buf_size - 1);
17101716
Py_END_ALLOW_THREADS
17111717
break;
17121718
case 1:
17131719
if (!PyArg_ParseTuple(args,"i;n", &n))
1714-
return NULL;
1720+
goto error;
17151721
if (n < 0) {
17161722
PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative");
1717-
return NULL;
1723+
goto error;
17181724
}
17191725
Py_BEGIN_ALLOW_THREADS
1720-
rtn2 = wgetnstr(self->win, rtn, Py_MIN(n, 1023));
1726+
rtn = wgetnstr(self->win, buf, Py_MIN(n, max_buf_size - 1));
17211727
Py_END_ALLOW_THREADS
17221728
break;
17231729
case 2:
17241730
if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x))
1725-
return NULL;
1731+
goto error;
17261732
Py_BEGIN_ALLOW_THREADS
17271733
#ifdef STRICT_SYSV_CURSES
1728-
rtn2 = wmove(self->win,y,x)==ERR ? ERR : wgetnstr(self->win, rtn, 1023);
1734+
rtn = wmove(self->win,y,x)==ERR ? ERR : wgetnstr(self->win, rtn, max_buf_size - 1);
17291735
#else
1730-
rtn2 = mvwgetnstr(self->win,y,x,rtn, 1023);
1736+
rtn = mvwgetnstr(self->win,y,x,buf, max_buf_size - 1);
17311737
#endif
17321738
Py_END_ALLOW_THREADS
17331739
break;
17341740
case 3:
17351741
if (!PyArg_ParseTuple(args,"iii;y,x,n", &y, &x, &n))
1736-
return NULL;
1742+
goto error;
17371743
if (n < 0) {
17381744
PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative");
1739-
return NULL;
1745+
goto error;
17401746
}
17411747
#ifdef STRICT_SYSV_CURSES
17421748
Py_BEGIN_ALLOW_THREADS
1743-
rtn2 = wmove(self->win,y,x)==ERR ? ERR :
1744-
wgetnstr(self->win, rtn, Py_MIN(n, 1023));
1749+
rtn = wmove(self->win,y,x)==ERR ? ERR :
1750+
wgetnstr(self->win, rtn, Py_MIN(n, max_buf_size - 1));
17451751
Py_END_ALLOW_THREADS
17461752
#else
17471753
Py_BEGIN_ALLOW_THREADS
1748-
rtn2 = mvwgetnstr(self->win, y, x, rtn, Py_MIN(n, 1023));
1754+
rtn = mvwgetnstr(self->win, y, x, buf, Py_MIN(n, max_buf_size - 1));
17491755
Py_END_ALLOW_THREADS
17501756
#endif
17511757
break;
17521758
default:
17531759
PyErr_SetString(PyExc_TypeError, "getstr requires 0 to 3 arguments");
1760+
goto error;
1761+
}
1762+
1763+
if (rtn == ERR) {
1764+
Py_DECREF(result);
1765+
return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
1766+
}
1767+
1768+
if (_PyBytes_Resize(&result, strlen(buf)) < 0) {
17541769
return NULL;
17551770
}
1756-
if (rtn2 == ERR)
1757-
rtn[0] = 0;
1758-
return PyBytes_FromString(rtn);
1771+
1772+
return result;
1773+
1774+
error:
1775+
Py_DECREF(result);
1776+
return NULL;
17591777
}
17601778

17611779
/*[clinic input]
@@ -1889,7 +1907,7 @@ _curses.window.instr
18891907
x: int
18901908
X-coordinate.
18911909
]
1892-
n: int = 1023
1910+
n: int = 2047
18931911
Maximal number of characters.
18941912
/
18951913
@@ -1906,43 +1924,61 @@ PyCursesWindow_InStr(PyObject *op, PyObject *args)
19061924
PyCursesWindowObject *self = _PyCursesWindowObject_CAST(op);
19071925

19081926
int x, y, n;
1909-
char rtn[1024]; /* This should be big enough.. I hope */
1910-
int rtn2;
1927+
int rtn;
1928+
1929+
/* could make the buffer size larger/dynamic */
1930+
Py_ssize_t max_buf_size = 2048;
1931+
PyObject *result = PyBytes_FromStringAndSize(NULL, max_buf_size);
1932+
if (result == NULL)
1933+
return NULL;
1934+
char *buf = PyBytes_AS_STRING(result);
19111935

19121936
switch (PyTuple_Size(args)) {
19131937
case 0:
1914-
rtn2 = winnstr(self->win,rtn, 1023);
1938+
rtn = winnstr(self->win, buf, max_buf_size - 1);
19151939
break;
19161940
case 1:
19171941
if (!PyArg_ParseTuple(args,"i;n", &n))
1918-
return NULL;
1942+
goto error;
19191943
if (n < 0) {
19201944
PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative");
1921-
return NULL;
1945+
goto error;
19221946
}
1923-
rtn2 = winnstr(self->win, rtn, Py_MIN(n, 1023));
1947+
rtn = winnstr(self->win, buf, Py_MIN(n, max_buf_size - 1));
19241948
break;
19251949
case 2:
19261950
if (!PyArg_ParseTuple(args,"ii;y,x",&y,&x))
1927-
return NULL;
1928-
rtn2 = mvwinnstr(self->win,y,x,rtn,1023);
1951+
goto error;
1952+
rtn = mvwinnstr(self->win, y, x, buf, max_buf_size - 1);
19291953
break;
19301954
case 3:
19311955
if (!PyArg_ParseTuple(args, "iii;y,x,n", &y, &x, &n))
1932-
return NULL;
1956+
goto error;
19331957
if (n < 0) {
19341958
PyErr_SetString(PyExc_ValueError, "'n' must be nonnegative");
1935-
return NULL;
1959+
goto error;
19361960
}
1937-
rtn2 = mvwinnstr(self->win, y, x, rtn, Py_MIN(n,1023));
1961+
rtn = mvwinnstr(self->win, y, x, buf, Py_MIN(n, max_buf_size - 1));
19381962
break;
19391963
default:
19401964
PyErr_SetString(PyExc_TypeError, "instr requires 0 or 3 arguments");
1965+
goto error;
1966+
}
1967+
1968+
if (rtn == ERR) {
1969+
Py_DECREF(result);
1970+
return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES);
1971+
}
1972+
1973+
if (_PyBytes_Resize(&result, strlen(buf)) < 0) {
19411974
return NULL;
19421975
}
1943-
if (rtn2 == ERR)
1944-
rtn[0] = 0;
1945-
return PyBytes_FromString(rtn);
1976+
1977+
return result;
1978+
1979+
error:
1980+
Py_DECREF(result);
1981+
return NULL;
19461982
}
19471983

19481984
/*[clinic input]

0 commit comments

Comments
 (0)