Kaydet (Commit) f7199578 authored tarafından Antoine Pitrou's avatar Antoine Pitrou

#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.
üst db26f7c1
...@@ -48,6 +48,15 @@ PyAPI_FUNC(void *) PyCObject_Import(char *module_name, char *cobject_name); ...@@ -48,6 +48,15 @@ PyAPI_FUNC(void *) PyCObject_Import(char *module_name, char *cobject_name);
/* Modify a C object. Fails (==0) if object has a destructor. */ /* Modify a C object. Fails (==0) if object has a destructor. */
PyAPI_FUNC(int) PyCObject_SetVoidPtr(PyObject *self, void *cobj); PyAPI_FUNC(int) PyCObject_SetVoidPtr(PyObject *self, void *cobj);
typedef struct {
PyObject_HEAD
void *cobject;
void *desc;
void (*destructor)(void *);
} PyCObject;
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
......
...@@ -12,6 +12,10 @@ What's New in Python 3.0 release candidate 1 ...@@ -12,6 +12,10 @@ What's New in Python 3.0 release candidate 1
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #3668: Fix a memory leak with the "s*" argument parser in
PyArg_ParseTuple and friends, which occurred when the argument for "s*"
was correctly parsed but parsing of subsequent arguments failed.
- Issue #3611: An exception __context__ could be cleared in a complex pattern - Issue #3611: An exception __context__ could be cleared in a complex pattern
involving a __del__ method re-raising an exception. involving a __del__ method re-raising an exception.
......
...@@ -9,13 +9,6 @@ ...@@ -9,13 +9,6 @@
typedef void (*destructor1)(void *); typedef void (*destructor1)(void *);
typedef void (*destructor2)(void *, void*); typedef void (*destructor2)(void *, void*);
typedef struct {
PyObject_HEAD
void *cobject;
void *desc;
void (*destructor)(void *);
} PyCObject;
PyObject * PyObject *
PyCObject_FromVoidPtr(void *cobj, void (*destr)(void *)) PyCObject_FromVoidPtr(void *cobj, void (*destr)(void *))
{ {
......
...@@ -139,24 +139,35 @@ _PyArg_VaParse_SizeT(PyObject *args, char *format, va_list va) ...@@ -139,24 +139,35 @@ _PyArg_VaParse_SizeT(PyObject *args, char *format, va_list va)
/* Handle cleanup of allocated memory in case of exception */ /* Handle cleanup of allocated memory in case of exception */
static void
cleanup_ptr(void *ptr)
{
PyMem_FREE(ptr);
}
static void
cleanup_buffer(void *ptr)
{
PyBuffer_Release((Py_buffer *) ptr);
}
static int static int
addcleanup(void *ptr, PyObject **freelist) addcleanup(void *ptr, PyObject **freelist, void (*destr)(void *))
{ {
PyObject *cobj; PyObject *cobj;
if (!*freelist) { if (!*freelist) {
*freelist = PyList_New(0); *freelist = PyList_New(0);
if (!*freelist) { if (!*freelist) {
PyMem_FREE(ptr); destr(ptr);
return -1; return -1;
} }
} }
cobj = PyCObject_FromVoidPtr(ptr, NULL); cobj = PyCObject_FromVoidPtr(ptr, destr);
if (!cobj) { if (!cobj) {
PyMem_FREE(ptr); destr(ptr);
return -1; return -1;
} }
if (PyList_Append(*freelist, cobj)) { if (PyList_Append(*freelist, cobj)) {
PyMem_FREE(ptr);
Py_DECREF(cobj); Py_DECREF(cobj);
return -1; return -1;
} }
...@@ -167,15 +178,15 @@ addcleanup(void *ptr, PyObject **freelist) ...@@ -167,15 +178,15 @@ addcleanup(void *ptr, PyObject **freelist)
static int static int
cleanreturn(int retval, PyObject *freelist) cleanreturn(int retval, PyObject *freelist)
{ {
if (freelist) { if (freelist && retval != 0) {
if (retval == 0) { /* We were successful, reset the destructors so that they
Py_ssize_t len = PyList_GET_SIZE(freelist), i; don't get called. */
for (i = 0; i < len; i++) Py_ssize_t len = PyList_GET_SIZE(freelist), i;
PyMem_FREE(PyCObject_AsVoidPtr( for (i = 0; i < len; i++)
PyList_GET_ITEM(freelist, i))); ((PyCObject *) PyList_GET_ITEM(freelist, i))
} ->destructor = NULL;
Py_DECREF(freelist); }
} Py_XDECREF(freelist);
return retval; return retval;
} }
...@@ -807,6 +818,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, ...@@ -807,6 +818,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
if (getbuffer(arg, p, &buf) < 0) if (getbuffer(arg, p, &buf) < 0)
return converterr(buf, arg, msgbuf, bufsize); return converterr(buf, arg, msgbuf, bufsize);
} }
if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr(
"(cleanup problem)",
arg, msgbuf, bufsize);
}
format++; format++;
} else if (*format == '#') { } else if (*format == '#') {
void **p = (void **)va_arg(*p_va, char **); void **p = (void **)va_arg(*p_va, char **);
...@@ -856,6 +872,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, ...@@ -856,6 +872,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
if (getbuffer(arg, (Py_buffer*)p, &buf) < 0) if (getbuffer(arg, (Py_buffer*)p, &buf) < 0)
return converterr(buf, arg, msgbuf, bufsize); return converterr(buf, arg, msgbuf, bufsize);
format++; format++;
if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr(
"(cleanup problem)",
arg, msgbuf, bufsize);
}
break; break;
} }
count = convertbuffer(arg, p, &buf); count = convertbuffer(arg, p, &buf);
...@@ -889,6 +910,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, ...@@ -889,6 +910,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
if (getbuffer(arg, p, &buf) < 0) if (getbuffer(arg, p, &buf) < 0)
return converterr(buf, arg, msgbuf, bufsize); return converterr(buf, arg, msgbuf, bufsize);
} }
if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr(
"(cleanup problem)",
arg, msgbuf, bufsize);
}
format++; format++;
} else if (*format == '#') { /* any buffer-like object */ } else if (*format == '#') { /* any buffer-like object */
void **p = (void **)va_arg(*p_va, char **); void **p = (void **)va_arg(*p_va, char **);
...@@ -1094,7 +1120,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, ...@@ -1094,7 +1120,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
"(memory error)", "(memory error)",
arg, msgbuf, bufsize); arg, msgbuf, bufsize);
} }
if (addcleanup(*buffer, freelist)) { if (addcleanup(*buffer, freelist, cleanup_ptr)) {
Py_DECREF(s); Py_DECREF(s);
return converterr( return converterr(
"(cleanup problem)", "(cleanup problem)",
...@@ -1136,7 +1162,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, ...@@ -1136,7 +1162,7 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
return converterr("(memory error)", return converterr("(memory error)",
arg, msgbuf, bufsize); arg, msgbuf, bufsize);
} }
if (addcleanup(*buffer, freelist)) { if (addcleanup(*buffer, freelist, cleanup_ptr)) {
Py_DECREF(s); Py_DECREF(s);
return converterr("(cleanup problem)", return converterr("(cleanup problem)",
arg, msgbuf, bufsize); arg, msgbuf, bufsize);
...@@ -1249,6 +1275,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags, ...@@ -1249,6 +1275,11 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
PyErr_Clear(); PyErr_Clear();
return converterr("read-write buffer", arg, msgbuf, bufsize); return converterr("read-write buffer", arg, msgbuf, bufsize);
} }
if (addcleanup(p, freelist, cleanup_buffer)) {
return converterr(
"(cleanup problem)",
arg, msgbuf, bufsize);
}
if (!PyBuffer_IsContiguous((Py_buffer*)p, 'C')) if (!PyBuffer_IsContiguous((Py_buffer*)p, 'C'))
return converterr("contiguous buffer", arg, msgbuf, bufsize); return converterr("contiguous buffer", arg, msgbuf, bufsize);
break; break;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment