[pypy-commit] pypy py3.5: Normalize the PyMem_Xxx functions and macros to CPython 3.5's situation

arigo pypy.commits at gmail.com
Sun Feb 5 11:14:11 EST 2017


Author: Armin Rigo <arigo at tunes.org>
Branch: py3.5
Changeset: r89938:319cd6335518
Date: 2017-02-05 17:13 +0100
http://bitbucket.org/pypy/pypy/changeset/319cd6335518/

Log:	Normalize the PyMem_Xxx functions and macros to CPython 3.5's
	situation

diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -612,6 +612,9 @@
     'Py_FrozenFlag', 'Py_TabcheckFlag', 'Py_UnicodeFlag', 'Py_IgnoreEnvironmentFlag',
     'Py_DivisionWarningFlag', 'Py_DontWriteBytecodeFlag', 'Py_NoUserSiteDirectory',
     '_Py_QnewFlag', 'Py_Py3kWarningFlag', 'Py_HashRandomizationFlag', '_Py_PackageContext',
+
+    'PyMem_RawMalloc', 'PyMem_RawCalloc', 'PyMem_RawRealloc', 'PyMem_RawFree',
+    'PyMem_Malloc', 'PyMem_Calloc', 'PyMem_Realloc', 'PyMem_Free',
 ]
 TYPES = {}
 FORWARD_DECLS = []
diff --git a/pypy/module/cpyext/include/pymem.h b/pypy/module/cpyext/include/pymem.h
--- a/pypy/module/cpyext/include/pymem.h
+++ b/pypy/module/cpyext/include/pymem.h
@@ -7,17 +7,22 @@
 extern "C" {
 #endif
 
-#define PyMem_MALLOC(n)		malloc((n) ? (n) : 1)
-#define PyMem_REALLOC(p, n)	realloc((p), (n) ? (n) : 1)
-#define PyMem_FREE		free
+#ifndef Py_LIMITED_API
+PyAPI_FUNC(void *) PyMem_RawMalloc(size_t size);
+PyAPI_FUNC(void *) PyMem_RawCalloc(size_t nelem, size_t elsize);
+PyAPI_FUNC(void *) PyMem_RawRealloc(void *ptr, size_t new_size);
+PyAPI_FUNC(void) PyMem_RawFree(void *ptr);
+#endif
 
-PyAPI_FUNC(void *) PyMem_Malloc(size_t);
-#define PyMem_Free  PyMem_FREE
-#define PyMem_Realloc  PyMem_REALLOC
+PyAPI_FUNC(void *) PyMem_Malloc(size_t size);
+PyAPI_FUNC(void *) PyMem_Calloc(size_t nelem, size_t elsize);
+PyAPI_FUNC(void *) PyMem_Realloc(void *ptr, size_t new_size);
+PyAPI_FUNC(void) PyMem_Free(void *ptr);
 
-#define PyMem_RawMalloc PyMem_Malloc
-#define PyMem_RawFree PyMem_Free
-#define PyMem_RawRealloc PyMem_Realloc
+#define PyMem_MALLOC(n)         PyMem_Malloc(n)
+#define PyMem_REALLOC(p, n)     PyMem_Realloc(p, n)
+#define PyMem_FREE(p)           PyMem_Free(p)
+
 
 /*
  * Type-oriented memory interface
diff --git a/pypy/module/cpyext/src/pymem.c b/pypy/module/cpyext/src/pymem.c
--- a/pypy/module/cpyext/src/pymem.c
+++ b/pypy/module/cpyext/src/pymem.c
@@ -1,6 +1,86 @@
 #include <Python.h>
 
-void * PyMem_Malloc(size_t n)
+void *
+PyMem_RawMalloc(size_t size)
 {
-    return malloc((n) ? (n) : 1);
+    /*
+     * Limit ourselves to PY_SSIZE_T_MAX bytes to prevent security holes.
+     * Most python internals blindly use a signed Py_ssize_t to track
+     * things without checking for overflows or negatives.
+     * As size_t is unsigned, checking for size < 0 is not required.
+     */
+    if (size > (size_t)PY_SSIZE_T_MAX)
+        return NULL;
+    if (size == 0)
+        size = 1;
+    return malloc(size);
 }
+
+void *
+PyMem_RawCalloc(size_t nelem, size_t elsize)
+{
+    /* see PyMem_RawMalloc() */
+    if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize)
+        return NULL;
+    /* PyMem_RawCalloc(0, 0) means calloc(1, 1). Some systems would return NULL
+       for calloc(0, 0), which would be treated as an error. Some platforms
+       would return a pointer with no memory behind it, which would break
+       pymalloc.  To solve these problems, allocate an extra byte. */
+    if (nelem == 0 || elsize == 0) {
+        nelem = 1;
+        elsize = 1;
+    }
+    return calloc(nelem, elsize);
+}
+
+void*
+PyMem_RawRealloc(void *ptr, size_t size)
+{
+    /* see PyMem_RawMalloc() */
+    if (size > (size_t)PY_SSIZE_T_MAX)
+        return NULL;
+    if (size == 0)
+        size = 1;
+    return realloc(ptr, size);
+}
+
+void PyMem_RawFree(void *ptr)
+{
+    free(ptr);
+}
+
+
+/* the PyMem_Xxx functions are the same as PyMem_RawXxx in PyPy, for now */
+void *PyMem_Malloc(size_t size)
+{
+    if (size > (size_t)PY_SSIZE_T_MAX)
+        return NULL;
+    if (size == 0)
+        size = 1;
+    return malloc(size);
+}
+
+void *PyMem_Calloc(size_t nelem, size_t elsize)
+{
+    if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize)
+        return NULL;
+    if (nelem == 0 || elsize == 0) {
+        nelem = 1;
+        elsize = 1;
+    }
+    return calloc(nelem, elsize);
+}
+
+void* PyMem_Realloc(void *ptr, size_t size)
+{
+    if (size > (size_t)PY_SSIZE_T_MAX)
+        return NULL;
+    if (size == 0)
+        size = 1;
+    return realloc(ptr, size);
+}
+
+void PyMem_Free(void *ptr)
+{
+    free(ptr);
+}
diff --git a/pypy/module/cpyext/test/test_pymem.py b/pypy/module/cpyext/test/test_pymem.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/test_pymem.py
@@ -0,0 +1,34 @@
+from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+
+
+class AppTestPyMem(AppTestCpythonExtensionBase):
+    def test_pymem_alloc(self):
+        module = self.import_extension('foo', [
+            ("test", "METH_NOARGS",
+             """
+                int *a, *b;
+                a = PyMem_RawCalloc(4, 50);
+                if (a[49] != 0) {
+                    PyErr_SetString(PyExc_ValueError, "1");
+                    return NULL;
+                }
+                a[49] = 123456;
+                b = PyMem_RawRealloc(a, 2000);
+                b[499] = 789123;
+                PyMem_RawFree(b);
+
+                a = PyMem_Calloc(4, 50);
+                if (a[49] != 0) {
+                    PyErr_SetString(PyExc_ValueError, "2");
+                    return NULL;
+                }
+                a[49] = 123456;
+                b = PyMem_Realloc(a, 2000);
+                b[499] = 789123;
+                PyMem_Free(b);
+
+                Py_RETURN_NONE;
+             """),
+            ])
+        res = module.test()
+        assert res is None


More information about the pypy-commit mailing list