Skip to content

Commit 929d44e

Browse files
authored
gh-114685: PyBuffer_FillInfo() now raises on PyBUF_{READ,WRITE} (GH-114802)
1 parent da8f9fb commit 929d44e

File tree

4 files changed

+56
-5
lines changed

4 files changed

+56
-5
lines changed

Lib/test/test_buffer.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4591,6 +4591,27 @@ def test_c_buffer_invalid_flags(self):
45914591
self.assertRaises(SystemError, buf.__buffer__, PyBUF_READ)
45924592
self.assertRaises(SystemError, buf.__buffer__, PyBUF_WRITE)
45934593

4594+
@unittest.skipIf(_testcapi is None, "requires _testcapi")
4595+
def test_c_fill_buffer_invalid_flags(self):
4596+
# PyBuffer_FillInfo
4597+
source = b"abc"
4598+
self.assertRaises(SystemError, _testcapi.buffer_fill_info,
4599+
source, 0, PyBUF_READ)
4600+
self.assertRaises(SystemError, _testcapi.buffer_fill_info,
4601+
source, 0, PyBUF_WRITE)
4602+
4603+
@unittest.skipIf(_testcapi is None, "requires _testcapi")
4604+
def test_c_fill_buffer_readonly_and_writable(self):
4605+
source = b"abc"
4606+
with _testcapi.buffer_fill_info(source, 1, PyBUF_SIMPLE) as m:
4607+
self.assertEqual(bytes(m), b"abc")
4608+
self.assertTrue(m.readonly)
4609+
with _testcapi.buffer_fill_info(source, 0, PyBUF_WRITABLE) as m:
4610+
self.assertEqual(bytes(m), b"abc")
4611+
self.assertFalse(m.readonly)
4612+
self.assertRaises(BufferError, _testcapi.buffer_fill_info,
4613+
source, 1, PyBUF_WRITABLE)
4614+
45944615
def test_inheritance(self):
45954616
class A(bytearray):
45964617
def __buffer__(self, flags):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:c:func:`PyBuffer_FillInfo` now raises a :exc:`SystemError` if called with
2+
:c:macro:`PyBUF_READ` or :c:macro:`PyBUF_WRITE` as flags. These flags should
3+
only be used with the ``PyMemoryView_*`` C API.

Modules/_testcapimodule.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,26 @@ make_memoryview_from_NULL_pointer(PyObject *self, PyObject *Py_UNUSED(ignored))
12611261
return PyMemoryView_FromBuffer(&info);
12621262
}
12631263

1264+
static PyObject *
1265+
buffer_fill_info(PyObject *self, PyObject *args)
1266+
{
1267+
Py_buffer info;
1268+
const char *data;
1269+
Py_ssize_t size;
1270+
int readonly;
1271+
int flags;
1272+
1273+
if (!PyArg_ParseTuple(args, "s#ii:buffer_fill_info",
1274+
&data, &size, &readonly, &flags)) {
1275+
return NULL;
1276+
}
1277+
1278+
if (PyBuffer_FillInfo(&info, NULL, (void *)data, size, readonly, flags) < 0) {
1279+
return NULL;
1280+
}
1281+
return PyMemoryView_FromBuffer(&info);
1282+
}
1283+
12641284
static PyObject *
12651285
test_from_contiguous(PyObject* self, PyObject *Py_UNUSED(ignored))
12661286
{
@@ -3314,6 +3334,7 @@ static PyMethodDef TestMethods[] = {
33143334
{"eval_code_ex", eval_eval_code_ex, METH_VARARGS},
33153335
{"make_memoryview_from_NULL_pointer", make_memoryview_from_NULL_pointer,
33163336
METH_NOARGS},
3337+
{"buffer_fill_info", buffer_fill_info, METH_VARARGS},
33173338
{"crash_no_current_thread", crash_no_current_thread, METH_NOARGS},
33183339
{"test_current_tstate_matches", test_current_tstate_matches, METH_NOARGS},
33193340
{"run_in_subinterp", run_in_subinterp, METH_VARARGS},

Objects/abstract.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -767,11 +767,17 @@ PyBuffer_FillInfo(Py_buffer *view, PyObject *obj, void *buf, Py_ssize_t len,
767767
return -1;
768768
}
769769

770-
if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) &&
771-
(readonly == 1)) {
772-
PyErr_SetString(PyExc_BufferError,
773-
"Object is not writable.");
774-
return -1;
770+
if (flags != PyBUF_SIMPLE) { /* fast path */
771+
if (flags == PyBUF_READ || flags == PyBUF_WRITE) {
772+
PyErr_BadInternalCall();
773+
return -1;
774+
}
775+
if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) &&
776+
(readonly == 1)) {
777+
PyErr_SetString(PyExc_BufferError,
778+
"Object is not writable.");
779+
return -1;
780+
}
775781
}
776782

777783
view->obj = Py_XNewRef(obj);

0 commit comments

Comments
 (0)