[Python-checkins] r66058 - in python/trunk: Include/cobject.h Misc/NEWS Objects/cobject.c Python/getargs.c

antoine.pitrou python-checkins at python.org
Fri Aug 29 20:39:48 CEST 2008


Author: antoine.pitrou
Date: Fri Aug 29 20:39:48 2008
New Revision: 66058

Log:
#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.



Modified:
   python/trunk/Include/cobject.h
   python/trunk/Misc/NEWS
   python/trunk/Objects/cobject.c
   python/trunk/Python/getargs.c

Modified: python/trunk/Include/cobject.h
==============================================================================
--- python/trunk/Include/cobject.h	(original)
+++ python/trunk/Include/cobject.h	Fri Aug 29 20:39:48 2008
@@ -48,6 +48,15 @@
 /* Modify a C object. Fails (==0) if object has a destructor. */
 PyAPI_FUNC(int) PyCObject_SetVoidPtr(PyObject *self, void *cobj);
 
+
+typedef struct {
+    PyObject_HEAD
+    void *cobject;
+    void *desc;
+    void (*destructor)(void *);
+} PyCObject;
+
+
 #ifdef __cplusplus
 }
 #endif

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Fri Aug 29 20:39:48 2008
@@ -12,6 +12,10 @@
 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 #2534: speed up isinstance() and issubclass() by 50-70%, so as to 
   match Python 2.5 speed despite the __instancecheck__ / __subclasscheck__
   mechanism. In the process, fix a bug where isinstance() and issubclass(),

Modified: python/trunk/Objects/cobject.c
==============================================================================
--- python/trunk/Objects/cobject.c	(original)
+++ python/trunk/Objects/cobject.c	Fri Aug 29 20:39:48 2008
@@ -9,13 +9,6 @@
 typedef void (*destructor1)(void *);
 typedef void (*destructor2)(void *, void*);
 
-typedef struct {
-    PyObject_HEAD
-    void *cobject;
-    void *desc;
-    void (*destructor)(void *);
-} PyCObject;
-
 PyObject *
 PyCObject_FromVoidPtr(void *cobj, void (*destr)(void *))
 {

Modified: python/trunk/Python/getargs.c
==============================================================================
--- python/trunk/Python/getargs.c	(original)
+++ python/trunk/Python/getargs.c	Fri Aug 29 20:39:48 2008
@@ -139,24 +139,35 @@
 
 /* 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
-addcleanup(void *ptr, PyObject **freelist)
+addcleanup(void *ptr, PyObject **freelist, void (*destr)(void *))
 {
 	PyObject *cobj;
 	if (!*freelist) {
 		*freelist = PyList_New(0);
 		if (!*freelist) {
-			PyMem_FREE(ptr);
+			destr(ptr);
 			return -1;
 		}
 	}
-	cobj = PyCObject_FromVoidPtr(ptr, NULL);
+	cobj = PyCObject_FromVoidPtr(ptr, destr);
 	if (!cobj) {
-		PyMem_FREE(ptr);
+		destr(ptr);
 		return -1;
 	}
 	if (PyList_Append(*freelist, cobj)) {
-                PyMem_FREE(ptr);
 		Py_DECREF(cobj);
 		return -1;
 	}
@@ -167,15 +178,15 @@
 static int
 cleanreturn(int retval, PyObject *freelist)
 {
-	if (freelist) {
-		if (retval == 0) {
-			Py_ssize_t len = PyList_GET_SIZE(freelist), i;
-			for (i = 0; i < len; i++)
-                                PyMem_FREE(PyCObject_AsVoidPtr(
-                                		PyList_GET_ITEM(freelist, i)));
-		}
-		Py_DECREF(freelist);
+	if (freelist && retval != 0) {
+		/* We were successful, reset the destructors so that they
+		   don't get called. */
+		Py_ssize_t len = PyList_GET_SIZE(freelist), i;
+		for (i = 0; i < len; i++)
+			((PyCObject *) PyList_GET_ITEM(freelist, i))
+				->destructor = NULL;
 	}
+	Py_XDECREF(freelist);
 	return retval;
 }
 
@@ -798,6 +809,11 @@
 				if (getbuffer(arg, p, &buf) < 0)
 					return converterr(buf, arg, msgbuf, bufsize);
 			}
+			if (addcleanup(p, freelist, cleanup_buffer)) {
+				return converterr(
+					"(cleanup problem)",
+					arg, msgbuf, bufsize);
+			}
 			format++;
 		} else if (*format == '#') {
 			void **p = (void **)va_arg(*p_va, char **);
@@ -875,6 +891,11 @@
 				if (getbuffer(arg, p, &buf) < 0)
 					return converterr(buf, arg, msgbuf, bufsize);
 			}
+			if (addcleanup(p, freelist, cleanup_buffer)) {
+				return converterr(
+					"(cleanup problem)",
+					arg, msgbuf, bufsize);
+			}
 			format++;
 		} else if (*format == '#') { /* any buffer-like object */
 			void **p = (void **)va_arg(*p_va, char **);
@@ -1051,7 +1072,7 @@
 						"(memory error)",
 						arg, msgbuf, bufsize);
 				}
-				if (addcleanup(*buffer, freelist)) {
+				if (addcleanup(*buffer, freelist, cleanup_ptr)) {
 					Py_DECREF(s);
 					return converterr(
 						"(cleanup problem)",
@@ -1096,7 +1117,7 @@
 				return converterr("(memory error)",
 						  arg, msgbuf, bufsize);
 			}
-			if (addcleanup(*buffer, freelist)) {
+			if (addcleanup(*buffer, freelist, cleanup_ptr)) {
 				Py_DECREF(s);
 				return converterr("(cleanup problem)",
 						arg, msgbuf, bufsize);
@@ -1214,6 +1235,11 @@
 				PyErr_Clear();
 				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'))
 				return converterr("contiguous buffer", arg, msgbuf, bufsize);
 			break;


More information about the Python-checkins mailing list