Skip to content

Commit d4ae97b

Browse files
committed
python#3668: When PyArg_ParseTuple correctly parses a s* format, but raises an
exception afterwards (for a subsequent parameter), the user code will not call PyBuffer_Release() and memory will leak. Reviewed by Amaury Forgeot d'Arc.
1 parent a27e89b commit d4ae97b

4 files changed

Lines changed: 54 additions & 22 deletions

File tree

Include/cobject.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ PyAPI_FUNC(void *) PyCObject_Import(char *module_name, char *cobject_name);
4848
/* Modify a C object. Fails (==0) if object has a destructor. */
4949
PyAPI_FUNC(int) PyCObject_SetVoidPtr(PyObject *self, void *cobj);
5050

51+
52+
typedef struct {
53+
PyObject_HEAD
54+
void *cobject;
55+
void *desc;
56+
void (*destructor)(void *);
57+
} PyCObject;
58+
59+
5160
#ifdef __cplusplus
5261
}
5362
#endif

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ What's New in Python 2.6 release candidate 1?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #3668: Fix a memory leak with the "s*" argument parser in
16+
PyArg_ParseTuple and friends, which occurred when the argument for "s*"
17+
was correctly parsed but parsing of subsequent arguments failed.
18+
1519
- Issue #2534: speed up isinstance() and issubclass() by 50-70%, so as to
1620
match Python 2.5 speed despite the __instancecheck__ / __subclasscheck__
1721
mechanism. In the process, fix a bug where isinstance() and issubclass(),

Objects/cobject.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,6 @@
99
typedef void (*destructor1)(void *);
1010
typedef void (*destructor2)(void *, void*);
1111

12-
typedef struct {
13-
PyObject_HEAD
14-
void *cobject;
15-
void *desc;
16-
void (*destructor)(void *);
17-
} PyCObject;
18-
1912
PyObject *
2013
PyCObject_FromVoidPtr(void *cobj, void (*destr)(void *))
2114
{

Python/getargs.c

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -139,24 +139,35 @@ _PyArg_VaParse_SizeT(PyObject *args, char *format, va_list va)
139139

140140
/* Handle cleanup of allocated memory in case of exception */
141141

142+
static void
143+
cleanup_ptr(void *ptr)
144+
{
145+
PyMem_FREE(ptr);
146+
}
147+
148+
static void
149+
cleanup_buffer(void *ptr)
150+
{
151+
PyBuffer_Release((Py_buffer *) ptr);
152+
}
153+
142154
static int
143-
addcleanup(void *ptr, PyObject **freelist)
155+
addcleanup(void *ptr, PyObject **freelist, void (*destr)(void *))
144156
{
145157
PyObject *cobj;
146158
if (!*freelist) {
147159
*freelist = PyList_New(0);
148160
if (!*freelist) {
149-
PyMem_FREE(ptr);
161+
destr(ptr);
150162
return -1;
151163
}
152164
}
153-
cobj = PyCObject_FromVoidPtr(ptr, NULL);
165+
cobj = PyCObject_FromVoidPtr(ptr, destr);
154166
if (!cobj) {
155-
PyMem_FREE(ptr);
167+
destr(ptr);
156168
return -1;
157169
}
158170
if (PyList_Append(*freelist, cobj)) {
159-
PyMem_FREE(ptr);
160171
Py_DECREF(cobj);
161172
return -1;
162173
}
@@ -167,15 +178,15 @@ addcleanup(void *ptr, PyObject **freelist)
167178
static int
168179
cleanreturn(int retval, PyObject *freelist)
169180
{
170-
if (freelist) {
171-
if (retval == 0) {
172-
Py_ssize_t len = PyList_GET_SIZE(freelist), i;
173-
for (i = 0; i < len; i++)
174-
PyMem_FREE(PyCObject_AsVoidPtr(
175-
PyList_GET_ITEM(freelist, i)));
176-
}
177-
Py_DECREF(freelist);
181+
if (freelist && retval != 0) {
182+
/* We were successful, reset the destructors so that they
183+
don't get called. */
184+
Py_ssize_t len = PyList_GET_SIZE(freelist), i;
185+
for (i = 0; i < len; i++)
186+
((PyCObject *) PyList_GET_ITEM(freelist, i))
187+
->destructor = NULL;
178188
}
189+
Py_XDECREF(freelist);
179190
return retval;
180191
}
181192

@@ -798,6 +809,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
798809
if (getbuffer(arg, p, &buf) < 0)
799810
return converterr(buf, arg, msgbuf, bufsize);
800811
}
812+
if (addcleanup(p, freelist, cleanup_buffer)) {
813+
return converterr(
814+
"(cleanup problem)",
815+
arg, msgbuf, bufsize);
816+
}
801817
format++;
802818
} else if (*format == '#') {
803819
void **p = (void **)va_arg(*p_va, char **);
@@ -875,6 +891,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
875891
if (getbuffer(arg, p, &buf) < 0)
876892
return converterr(buf, arg, msgbuf, bufsize);
877893
}
894+
if (addcleanup(p, freelist, cleanup_buffer)) {
895+
return converterr(
896+
"(cleanup problem)",
897+
arg, msgbuf, bufsize);
898+
}
878899
format++;
879900
} else if (*format == '#') { /* any buffer-like object */
880901
void **p = (void **)va_arg(*p_va, char **);
@@ -1051,7 +1072,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
10511072
"(memory error)",
10521073
arg, msgbuf, bufsize);
10531074
}
1054-
if (addcleanup(*buffer, freelist)) {
1075+
if (addcleanup(*buffer, freelist, cleanup_ptr)) {
10551076
Py_DECREF(s);
10561077
return converterr(
10571078
"(cleanup problem)",
@@ -1096,7 +1117,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
10961117
return converterr("(memory error)",
10971118
arg, msgbuf, bufsize);
10981119
}
1099-
if (addcleanup(*buffer, freelist)) {
1120+
if (addcleanup(*buffer, freelist, cleanup_ptr)) {
11001121
Py_DECREF(s);
11011122
return converterr("(cleanup problem)",
11021123
arg, msgbuf, bufsize);
@@ -1214,6 +1235,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
12141235
PyErr_Clear();
12151236
return converterr("read-write buffer", arg, msgbuf, bufsize);
12161237
}
1238+
if (addcleanup(p, freelist, cleanup_buffer)) {
1239+
return converterr(
1240+
"(cleanup problem)",
1241+
arg, msgbuf, bufsize);
1242+
}
12171243
if (!PyBuffer_IsContiguous((Py_buffer*)p, 'C'))
12181244
return converterr("contiguous buffer", arg, msgbuf, bufsize);
12191245
break;

0 commit comments

Comments
 (0)