Issue #25498: Merge ctypes crash fix from 3.4 into 3.5

This commit is contained in:
Martin Panter 2015-11-13 22:12:12 +00:00
commit 0da4ac1f21
4 changed files with 72 additions and 20 deletions

View file

@ -38,11 +38,32 @@ class Test(unittest.TestCase):
del a; gc.collect(); gc.collect(); gc.collect() del a; gc.collect(); gc.collect(); gc.collect()
self.assertEqual(x[:], expected) self.assertEqual(x[:], expected)
with self.assertRaises(TypeError): with self.assertRaisesRegex(TypeError, "not writable"):
(c_char * 16).from_buffer(b"a" * 16) (c_char * 16).from_buffer(b"a" * 16)
with self.assertRaises(TypeError): with self.assertRaisesRegex(TypeError, "not writable"):
(c_char * 16).from_buffer(memoryview(b"a" * 16))
with self.assertRaisesRegex(TypeError, "not C contiguous"):
(c_char * 16).from_buffer(memoryview(bytearray(b"a" * 16))[::-1])
msg = "does not have the buffer interface"
with self.assertRaisesRegex(TypeError, msg):
(c_char * 16).from_buffer("a" * 16) (c_char * 16).from_buffer("a" * 16)
def test_fortran_contiguous(self):
try:
import _testbuffer
except ImportError as err:
self.skipTest(str(err))
flags = _testbuffer.ND_WRITABLE | _testbuffer.ND_FORTRAN
array = _testbuffer.ndarray(
[97] * 16, format="B", shape=[4, 4], flags=flags)
with self.assertRaisesRegex(TypeError, "not C contiguous"):
(c_char * 16).from_buffer(array)
array = memoryview(array)
self.assertTrue(array.f_contiguous)
self.assertFalse(array.c_contiguous)
with self.assertRaisesRegex(TypeError, "not C contiguous"):
(c_char * 16).from_buffer(array)
def test_from_buffer_with_offset(self): def test_from_buffer_with_offset(self):
a = array.array("i", range(16)) a = array.array("i", range(16))
x = (c_int * 15).from_buffer(a, sizeof(c_int)) x = (c_int * 15).from_buffer(a, sizeof(c_int))
@ -55,6 +76,12 @@ class Test(unittest.TestCase):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
(c_int * 1).from_buffer(a, 16 * sizeof(c_int)) (c_int * 1).from_buffer(a, 16 * sizeof(c_int))
def test_from_buffer_memoryview(self):
a = [c_char.from_buffer(memoryview(bytearray(b'a')))]
a.append(a)
del a
gc.collect() # Should not crash
def test_from_buffer_copy(self): def test_from_buffer_copy(self):
a = array.array("i", range(16)) a = array.array("i", range(16))
x = (c_int * 16).from_buffer_copy(a) x = (c_int * 16).from_buffer_copy(a)

View file

@ -398,6 +398,7 @@ Gökcen Eraslan
Stoffel Erasmus Stoffel Erasmus
Jürgen A. Erhard Jürgen A. Erhard
Michael Ernst Michael Ernst
Eryksun
Ben Escoto Ben Escoto
Andy Eskilsson Andy Eskilsson
André Espaze André Espaze

View file

@ -67,6 +67,10 @@ Core and Builtins
Library Library
------- -------
- Issue #25498: Fix a crash when garbage-collecting ctypes objects created
by wrapping a memoryview. This was a regression made in 3.5a1. Based
on patch by Eryksun.
- Issue #25584: Added "escape" to the __all__ list in the glob module. - Issue #25584: Added "escape" to the __all__ list in the glob module.
- Issue #25584: Fixed recursive glob() with patterns starting with '\*\*'. - Issue #25584: Fixed recursive glob() with patterns starting with '\*\*'.

View file

@ -463,45 +463,65 @@ KeepRef(CDataObject *target, Py_ssize_t index, PyObject *keep);
static PyObject * static PyObject *
CDataType_from_buffer(PyObject *type, PyObject *args) CDataType_from_buffer(PyObject *type, PyObject *args)
{ {
Py_buffer buffer; PyObject *obj;
PyObject *mv;
PyObject *result;
Py_buffer *buffer;
Py_ssize_t offset = 0; Py_ssize_t offset = 0;
PyObject *result, *mv;
StgDictObject *dict = PyType_stgdict(type); StgDictObject *dict = PyType_stgdict(type);
assert (dict); assert (dict);
if (!PyArg_ParseTuple(args, "w*|n:from_buffer", &buffer, &offset)) if (!PyArg_ParseTuple(args, "O|n:from_buffer", &obj, &offset))
return NULL; return NULL;
mv = PyMemoryView_FromObject(obj);
if (mv == NULL)
return NULL;
buffer = PyMemoryView_GET_BUFFER(mv);
if (buffer->readonly) {
PyErr_SetString(PyExc_TypeError,
"underlying buffer is not writable");
Py_DECREF(mv);
return NULL;
}
if (!PyBuffer_IsContiguous(buffer, 'C')) {
PyErr_SetString(PyExc_TypeError,
"underlying buffer is not C contiguous");
Py_DECREF(mv);
return NULL;
}
if (offset < 0) { if (offset < 0) {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"offset cannot be negative"); "offset cannot be negative");
PyBuffer_Release(&buffer); Py_DECREF(mv);
return NULL; return NULL;
} }
if (dict->size > buffer.len - offset) {
if (dict->size > buffer->len - offset) {
PyErr_Format(PyExc_ValueError, PyErr_Format(PyExc_ValueError,
"Buffer size too small (%zd instead of at least %zd bytes)", "Buffer size too small "
buffer.len, dict->size + offset); "(%zd instead of at least %zd bytes)",
PyBuffer_Release(&buffer); buffer->len, dict->size + offset);
Py_DECREF(mv);
return NULL; return NULL;
} }
result = PyCData_AtAddress(type, (char *)buffer.buf + offset); result = PyCData_AtAddress(type, (char *)buffer->buf + offset);
if (result == NULL) { if (result == NULL) {
PyBuffer_Release(&buffer); Py_DECREF(mv);
return NULL; return NULL;
} }
mv = PyMemoryView_FromBuffer(&buffer); if (-1 == KeepRef((CDataObject *)result, -1, mv)) {
if (mv == NULL) { Py_DECREF(result);
PyBuffer_Release(&buffer);
return NULL; return NULL;
} }
/* Hack the memoryview so that it will release the buffer. */
((PyMemoryViewObject *)mv)->mbuf->master.obj = buffer.obj;
((PyMemoryViewObject *)mv)->view.obj = buffer.obj;
if (-1 == KeepRef((CDataObject *)result, -1, mv))
result = NULL;
return result; return result;
} }