[pypy-commit] pypy default: Fix for the issue of abuse of PyCapsules, relying on immediate

arigo noreply at buildbot.pypy.org
Wed Jun 13 15:48:34 CEST 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r55645:c26dc70ee340
Date: 2012-06-13 15:48 +0200
http://bitbucket.org/pypy/pypy/changeset/c26dc70ee340/

Log:	Fix for the issue of abuse of PyCapsules, relying on immediate
	destruction, as CPython does. This problem was first described in
	https://bazaar.launchpad.net/~exarkun/pyopenssl/trunk/revision/166.
	The fix is rather obvious and consist in a *negative* total amount
	of lines :-/

diff --git a/pypy/module/cpyext/src/getargs.c b/pypy/module/cpyext/src/getargs.c
--- a/pypy/module/cpyext/src/getargs.c
+++ b/pypy/module/cpyext/src/getargs.c
@@ -24,14 +24,15 @@
 
 
 /* Forward */
+typedef struct freelist_s freelist_t;
 static int vgetargs1(PyObject *, const char *, va_list *, int);
 static void seterror(int, const char *, int *, const char *, const char *);
 static char *convertitem(PyObject *, const char **, va_list *, int, int *,
-                         char *, size_t, PyObject **);
+                         char *, size_t, freelist_t **);
 static char *converttuple(PyObject *, const char **, va_list *, int,
-                          int *, char *, size_t, int, PyObject **);
+                          int *, char *, size_t, int, freelist_t **);
 static char *convertsimple(PyObject *, const char **, va_list *, int, char *,
-                           size_t, PyObject **);
+                           size_t, freelist_t **);
 static Py_ssize_t convertbuffer(PyObject *, void **p, char **);
 static int getbuffer(PyObject *, Py_buffer *, char**);
 
@@ -128,72 +129,45 @@
 
 /* Handle cleanup of allocated memory in case of exception */
 
-#define GETARGS_CAPSULE_NAME_CLEANUP_PTR "getargs.cleanup_ptr"
-#define GETARGS_CAPSULE_NAME_CLEANUP_BUFFER "getargs.cleanup_buffer"
+typedef void (*cleanup_fn)(void *);
 
-static void
-cleanup_ptr(PyObject *self)
-{
-    void *ptr = PyCapsule_GetPointer(self, GETARGS_CAPSULE_NAME_CLEANUP_PTR);
-    if (ptr) {
-      PyMem_FREE(ptr);
-    }
-}
+struct freelist_s {
+    void *ptr;
+    cleanup_fn destr;
+    struct freelist_s *next;
+};
 
-static void
-cleanup_buffer(PyObject *self)
-{
-    Py_buffer *ptr = (Py_buffer *)PyCapsule_GetPointer(self, GETARGS_CAPSULE_NAME_CLEANUP_BUFFER);
-    if (ptr) {
-        PyBuffer_Release(ptr);
-    }
-}
+#define cleanup_ptr ((cleanup_fn)PyMem_FREE)
+#define cleanup_buffer ((cleanup_fn)PyBuffer_Release)
 
 static int
-addcleanup(void *ptr, PyObject **freelist, PyCapsule_Destructor destr)
+addcleanup(void *ptr, freelist_t **freelist, cleanup_fn destr)
 {
-    PyObject *cobj;
-    const char *name;
-
-    if (!*freelist) {
-        *freelist = PyList_New(0);
-        if (!*freelist) {
-            destr(ptr);
-            return -1;
-        }
-    }
-
-    if (destr == cleanup_ptr) {
-        name = GETARGS_CAPSULE_NAME_CLEANUP_PTR;
-    } else if (destr == cleanup_buffer) {
-        name = GETARGS_CAPSULE_NAME_CLEANUP_BUFFER;
-    } else {
-        return -1;
-    }
-    cobj = PyCapsule_New(ptr, name, destr);
-    if (!cobj) {
+    freelist_t *node = PyMem_MALLOC(sizeof(freelist_t));
+    if (!node) {
         destr(ptr);
         return -1;
     }
-    if (PyList_Append(*freelist, cobj)) {
-        Py_DECREF(cobj);
-        return -1;
-    }
-    Py_DECREF(cobj);
+    node->ptr = ptr;
+    node->destr = destr;
+    node->next = *freelist;
+    *freelist = node;
     return 0;
 }
 
 static int
-cleanreturn(int retval, PyObject *freelist)
+cleanreturn(int retval, freelist_t *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++)
-            PyCapsule_SetDestructor(PyList_GET_ITEM(freelist, i), NULL);
+    freelist_t *next;
+    while (freelist != NULL) {
+        if (retval == 0) {
+            /* Leaving with an error */
+            freelist->destr(freelist->ptr);
+        }
+        next = freelist->next;
+        PyMem_FREE(freelist);
+        freelist = next;
     }
-    Py_XDECREF(freelist);
     return retval;
 }
 
@@ -212,7 +186,7 @@
     const char *formatsave = format;
     Py_ssize_t i, len;
     char *msg;
-    PyObject *freelist = NULL;
+    freelist_t *freelist = NULL;
     int compat = flags & FLAG_COMPAT;
 
     assert(compat || (args != (PyObject*)NULL));
@@ -412,7 +386,7 @@
 static char *
 converttuple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
              int *levels, char *msgbuf, size_t bufsize, int toplevel,
-             PyObject **freelist)
+             freelist_t **freelist)
 {
     int level = 0;
     int n = 0;
@@ -488,7 +462,7 @@
 
 static char *
 convertitem(PyObject *arg, const char **p_format, va_list *p_va, int flags,
-            int *levels, char *msgbuf, size_t bufsize, PyObject **freelist)
+            int *levels, char *msgbuf, size_t bufsize, freelist_t **freelist)
 {
     char *msg;
     const char *format = *p_format;
@@ -569,7 +543,7 @@
 
 static char *
 convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
-              char *msgbuf, size_t bufsize, PyObject **freelist)
+              char *msgbuf, size_t bufsize, freelist_t **freelist)
 {
     /* For # codes */
 #define FETCH_SIZE      int *q=NULL;Py_ssize_t *q2=NULL;\
@@ -1534,7 +1508,8 @@
     const char *fname, *msg, *custom_msg, *keyword;
     int min = INT_MAX;
     int i, len, nargs, nkeywords;
-    PyObject *freelist = NULL, *current_arg;
+    freelist_t *freelist = NULL;
+    PyObject *current_arg;
 
     assert(args != NULL && PyTuple_Check(args));
     assert(keywords == NULL || PyDict_Check(keywords));
diff --git a/pypy/module/cpyext/test/test_getargs.py b/pypy/module/cpyext/test/test_getargs.py
--- a/pypy/module/cpyext/test/test_getargs.py
+++ b/pypy/module/cpyext/test/test_getargs.py
@@ -144,6 +144,31 @@
         assert 'foo\0bar\0baz' == pybuffer(buffer('foo\0bar\0baz'))
 
 
+    def test_pyarg_parse_string_fails(self):
+        """
+        Test the failing case of PyArg_ParseTuple(): it must not keep
+        a reference on the PyObject passed in.
+        """
+        pybuffer = self.import_parser(
+            '''
+            Py_buffer buf1, buf2, buf3;
+            PyObject *result;
+            if (!PyArg_ParseTuple(args, "s*s*s*", &buf1, &buf2, &buf3)) {
+                return NULL;
+            }
+            Py_FatalError("should not get there");
+            return NULL;
+            ''')
+        freed = []
+        class freestring(str):
+            def __del__(self):
+                freed.append('x')
+        raises(TypeError, pybuffer,
+               freestring("string"), freestring("other string"), 42)
+        import gc; gc.collect()
+        assert freed == ['x', 'x']
+
+
     def test_pyarg_parse_charbuf_and_length(self):
         """
         The `t#` format specifier can be used to parse a read-only 8-bit


More information about the pypy-commit mailing list