Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2fe9bac

Browse files
committed
Close #16742: Fix misuse of memory allocations in PyOS_Readline()
The GIL must be held to call PyMem_Malloc(), whereas PyOS_Readline() releases the GIL to read input. The result of the C callback PyOS_ReadlineFunctionPointer must now be a string allocated by PyMem_RawMalloc() or PyMem_RawRealloc() (or NULL if an error occurred), instead of a string allocated by PyMem_Malloc() or PyMem_Realloc(). Fixing this issue was required to setup a hook on PyMem_Malloc(), for example using the tracemalloc module. PyOS_Readline() copies the result of PyOS_ReadlineFunctionPointer() into a new buffer allocated by PyMem_Malloc(). So the public API of PyOS_Readline() does not change.
1 parent 6cf185d commit 2fe9bac

5 files changed

Lines changed: 41 additions & 8 deletions

File tree

Doc/c-api/veryhigh.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,14 @@ the same library that the Python runtime is using.
166166
resulting string. For example, The :mod:`readline` module sets
167167
this hook to provide line-editing and tab-completion features.
168168
169+
The result must be a string allocated by :c:func:`PyMem_RawMalloc` or
170+
:c:func:`PyMem_RawRealloc`, or *NULL* if an error occurred.
171+
172+
.. versionchanged:: 3.4
173+
The result must be allocated by :c:func:`PyMem_RawMalloc` or
174+
:c:func:`PyMem_RawRealloc`, instead of being allocated by
175+
:c:func:`PyMem_Malloc` or :c:func:`PyMem_Realloc`.
176+
169177
170178
.. c:function:: struct _node* PyParser_SimpleParseString(const char *str, int start)
171179

Doc/whatsnew/3.4.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,3 +587,9 @@ that may require changes to your code.
587587
attribute in the chain referring to the innermost function. Introspection
588588
libraries that assumed the previous behaviour was intentional can use
589589
:func:`inspect.unwrap` to gain equivalent behaviour.
590+
591+
* (C API) The result of the :c:var:`PyOS_ReadlineFunctionPointer` callback must
592+
now be a string allocated by :c:func:`PyMem_RawMalloc` or
593+
:c:func:`PyMem_RawRealloc`, or *NULL* if an error occurred, instead of a
594+
string allocated by :c:func:`PyMem_Malloc` or :c:func:`PyMem_Realloc`.
595+

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ Projected release date: 2013-10-20
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #16742: The result of the C callback PyOS_ReadlineFunctionPointer must
14+
now be a string allocated by PyMem_RawMalloc() or PyMem_RawRealloc() (or NULL
15+
if an error occurred), instead of a string allocated by PyMem_Malloc() or
16+
PyMem_Realloc().
17+
1318
- Issue #19199: Remove ``PyThreadState.tick_counter`` field
1419

1520
- Fix macro expansion of _PyErr_OCCURRED(), and make sure to use it in at

Modules/readline.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,7 +1176,7 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
11761176

11771177
/* We got an EOF, return a empty string. */
11781178
if (p == NULL) {
1179-
p = PyMem_Malloc(1);
1179+
p = PyMem_RawMalloc(1);
11801180
if (p != NULL)
11811181
*p = '\0';
11821182
RESTORE_LOCALE(saved_locale)
@@ -1204,7 +1204,7 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
12041204
/* Copy the malloc'ed buffer into a PyMem_Malloc'ed one and
12051205
release the original. */
12061206
q = p;
1207-
p = PyMem_Malloc(n+2);
1207+
p = PyMem_RawMalloc(n+2);
12081208
if (p != NULL) {
12091209
strncpy(p, q, n);
12101210
p[n] = '\n';

Parser/myreadline.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,18 +113,22 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
113113
{
114114
size_t n;
115115
char *p, *pr;
116+
116117
n = 100;
117-
if ((p = (char *)PyMem_MALLOC(n)) == NULL)
118+
p = (char *)PyMem_RawMalloc(n);
119+
if (p == NULL)
118120
return NULL;
121+
119122
fflush(sys_stdout);
120123
if (prompt)
121124
fprintf(stderr, "%s", prompt);
122125
fflush(stderr);
126+
123127
switch (my_fgets(p, (int)n, sys_stdin)) {
124128
case 0: /* Normal case */
125129
break;
126130
case 1: /* Interrupt */
127-
PyMem_FREE(p);
131+
PyMem_RawFree(p);
128132
return NULL;
129133
case -1: /* EOF */
130134
case -2: /* Error */
@@ -140,7 +144,7 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
140144
PyErr_SetString(PyExc_OverflowError, "input line too long");
141145
return NULL;
142146
}
143-
pr = (char *)PyMem_REALLOC(p, n + incr);
147+
pr = (char *)PyMem_RawRealloc(p, n + incr);
144148
if (pr == NULL) {
145149
PyMem_FREE(p);
146150
PyErr_NoMemory();
@@ -151,7 +155,7 @@ PyOS_StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
151155
break;
152156
n += strlen(p+n);
153157
}
154-
pr = (char *)PyMem_REALLOC(p, n+1);
158+
pr = (char *)PyMem_RawRealloc(p, n+1);
155159
if (pr == NULL) {
156160
PyMem_FREE(p);
157161
PyErr_NoMemory();
@@ -174,7 +178,8 @@ char *(*PyOS_ReadlineFunctionPointer)(FILE *, FILE *, char *);
174178
char *
175179
PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
176180
{
177-
char *rv;
181+
char *rv, *res;
182+
size_t len;
178183

179184
if (_PyOS_ReadlineTState == PyThreadState_GET()) {
180185
PyErr_SetString(PyExc_RuntimeError,
@@ -221,5 +226,14 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt)
221226

222227
_PyOS_ReadlineTState = NULL;
223228

224-
return rv;
229+
if (rv == NULL)
230+
return NULL;
231+
232+
len = strlen(rv) + 1;
233+
res = PyMem_Malloc(len);
234+
if (res != NULL)
235+
memcpy(res, rv, len);
236+
PyMem_RawFree(rv);
237+
238+
return res;
225239
}

0 commit comments

Comments
 (0)