]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-114685: PyBuffer_FillInfo() now raises on PyBUF_{READ,WRITE} (GH-114802)
authorNikita Sobolev <mail@sobolevn.me>
Sun, 4 Feb 2024 19:16:43 +0000 (22:16 +0300)
committerGitHub <noreply@github.com>
Sun, 4 Feb 2024 19:16:43 +0000 (19:16 +0000)
Lib/test/test_buffer.py
Misc/NEWS.d/next/C API/2024-01-31-15-43-35.gh-issue-114685.n7aRmX.rst [new file with mode: 0644]
Modules/_testcapimodule.c
Objects/abstract.c

index 535b795f508a2420802d2f34e463f2b1dbe6eed7..5b1b95b9c82064cbc058b31158466ffe963f8a58 100644 (file)
@@ -4591,6 +4591,27 @@ class TestPythonBufferProtocol(unittest.TestCase):
         self.assertRaises(SystemError, buf.__buffer__, PyBUF_READ)
         self.assertRaises(SystemError, buf.__buffer__, PyBUF_WRITE)
 
+    @unittest.skipIf(_testcapi is None, "requires _testcapi")
+    def test_c_fill_buffer_invalid_flags(self):
+        # PyBuffer_FillInfo
+        source = b"abc"
+        self.assertRaises(SystemError, _testcapi.buffer_fill_info,
+                          source, 0, PyBUF_READ)
+        self.assertRaises(SystemError, _testcapi.buffer_fill_info,
+                          source, 0, PyBUF_WRITE)
+
+    @unittest.skipIf(_testcapi is None, "requires _testcapi")
+    def test_c_fill_buffer_readonly_and_writable(self):
+        source = b"abc"
+        with _testcapi.buffer_fill_info(source, 1, PyBUF_SIMPLE) as m:
+            self.assertEqual(bytes(m), b"abc")
+            self.assertTrue(m.readonly)
+        with _testcapi.buffer_fill_info(source, 0, PyBUF_WRITABLE) as m:
+            self.assertEqual(bytes(m), b"abc")
+            self.assertFalse(m.readonly)
+        self.assertRaises(BufferError, _testcapi.buffer_fill_info,
+                          source, 1, PyBUF_WRITABLE)
+
     def test_inheritance(self):
         class A(bytearray):
             def __buffer__(self, flags):
diff --git a/Misc/NEWS.d/next/C API/2024-01-31-15-43-35.gh-issue-114685.n7aRmX.rst b/Misc/NEWS.d/next/C API/2024-01-31-15-43-35.gh-issue-114685.n7aRmX.rst
new file mode 100644 (file)
index 0000000..76ff006
--- /dev/null
@@ -0,0 +1,3 @@
+:c:func:`PyBuffer_FillInfo` now raises a :exc:`SystemError` if called with
+:c:macro:`PyBUF_READ` or :c:macro:`PyBUF_WRITE` as flags. These flags should
+only be used with the ``PyMemoryView_*`` C API.
index 6def680190b1a637eabd3ef491ac6bda5baf2134..e67de3eeb6e17eb90cf730e1b47f6399a307d5c2 100644 (file)
@@ -1261,6 +1261,26 @@ make_memoryview_from_NULL_pointer(PyObject *self, PyObject *Py_UNUSED(ignored))
     return PyMemoryView_FromBuffer(&info);
 }
 
+static PyObject *
+buffer_fill_info(PyObject *self, PyObject *args)
+{
+    Py_buffer info;
+    const char *data;
+    Py_ssize_t size;
+    int readonly;
+    int flags;
+
+    if (!PyArg_ParseTuple(args, "s#ii:buffer_fill_info",
+                          &data, &size, &readonly, &flags)) {
+        return NULL;
+    }
+
+    if (PyBuffer_FillInfo(&info, NULL, (void *)data, size, readonly, flags) < 0) {
+        return NULL;
+    }
+    return PyMemoryView_FromBuffer(&info);
+}
+
 static PyObject *
 test_from_contiguous(PyObject* self, PyObject *Py_UNUSED(ignored))
 {
@@ -3314,6 +3334,7 @@ static PyMethodDef TestMethods[] = {
     {"eval_code_ex",            eval_eval_code_ex,               METH_VARARGS},
     {"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer,
      METH_NOARGS},
+    {"buffer_fill_info",        buffer_fill_info,                METH_VARARGS},
     {"crash_no_current_thread", crash_no_current_thread,         METH_NOARGS},
     {"test_current_tstate_matches", test_current_tstate_matches, METH_NOARGS},
     {"run_in_subinterp",        run_in_subinterp,                METH_VARARGS},
index daf04eb4ab2cda37a5a097c1700fd32e350c3289..07d4b89fe188c8450595a4183e41427e03af8aad 100644 (file)
@@ -767,11 +767,17 @@ PyBuffer_FillInfo(Py_buffer *view, PyObject *obj, void *buf, Py_ssize_t len,
         return -1;
     }
 
-    if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) &&
-        (readonly == 1)) {
-        PyErr_SetString(PyExc_BufferError,
-                        "Object is not writable.");
-        return -1;
+    if (flags != PyBUF_SIMPLE) {  /* fast path */
+        if (flags == PyBUF_READ || flags == PyBUF_WRITE) {
+            PyErr_BadInternalCall();
+            return -1;
+        }
+        if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) &&
+            (readonly == 1)) {
+            PyErr_SetString(PyExc_BufferError,
+                            "Object is not writable.");
+            return -1;
+        }
     }
 
     view->obj = Py_XNewRef(obj);