8000 BUG: add checks for some invalid structured dtypes. · numpy/numpy@9fe73dd · GitHub
[go: up one dir, main page]

Skip to content

Commit 9fe73dd

Browse files
committed
BUG: add checks for some invalid structured dtypes.
Fixes #2865.
1 parent 2075e1f commit 9fe73dd

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

doc/release/1.13.0-notes.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ Future Changes
2727
Compatibility notes
2828
===================
2929

30+
Tuple object dtypes
31+
~~~~~~~~~~~~~~~~~~~
32+
33+
Support has been removed for certain obscure dtypes that were unintentionally
34+
allowed, of the form ``(old_dtype, new_dtype)``, where either of the dtypes
35+
is or contains the ``object`` dtype. As an exception, dtypes of the form
36+
``(object, [('name', object)])`` are still supported due to evidence of
37+
existing use.
38+
3039
DeprecationWarning to error
3140
~~~~~~~~~~~~~~~~~~~~~~~~~~~
3241

numpy/core/src/multiarray/descriptor.c

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ _convert_from_tuple(PyObject *obj)
287287
type->elsize = itemsize;
288288
}
289289
}
290-
else if (PyDict_Check(val) || PyDictProxy_Check(val)) {
290+
else if (type->metadata && (PyDict_Check(val) || PyDictProxy_Check(val))) {
291291
/* Assume it's a metadata dictionary */
292292
if (PyDict_Merge(type->metadata, val, 0) == -1) {
293293
Py_DECREF(type);
@@ -772,6 +772,54 @@ _is_tuple_of_integers(PyObject *obj)
772772
return 1;
773773
}
774774

775+
/*
776+
* helper function for _use_inherit to disallow dtypes of the form
777+
* (old_dtype, new_dtype) where either of the dtypes contains python
778+
* objects - these dtypes are not useful and can be a source of segfaults,
779+
* when an attempt is made to interpret a python object as a different dtype
780+
* or vice versa
781+
* an exception is made for dtypes of the form ('O', [('name', 'O')]), which
782+
* people have been using to add a field to an object array without fields
783+
*/
784+
static int
785+
invalid_union_object_dtype(PyArray_Descr *new, PyArray_Descr *conv)
786+
{
787+
PyObject *name, *tup;
788+
PyArray_Descr *dtype;
789+
790+
if (!PyDataType_REFCHK(new) && !PyDataType_REFCHK(conv)) {
791+
return 0;
792+
}
793+
if (PyDataType_HASFIELDS(new) || new->kind != 'O') {
794+
goto fail;
795+
}
796+
if (!PyDataType_HASFIELDS(conv) || PyTuple_GET_SIZE(conv->names) != 1) {
797+
goto fail;
798+
}
799+
name = PyTuple_GET_ITEM(conv->names, 0);
800+
if (name == NULL) {
801+
return -1;
802+
}
803+
tup = PyDict_GetItem(conv->fields, name);
804+
if (tup == NULL) {
805+
return -1;
806+
}
807+
dtype = (PyArray_Descr *)PyTuple_GET_ITEM(tup, 0);
808+
if (dtype == NULL) {
809+
return -1;
810+
}
811+
if (dtype->kind != 'O') {
812+
goto fail;
813+
}
814+
return 0;
815+
816+
fail:
817+
PyErr_SetString(PyExc_ValueError,
818+
"dtypes of the form (old_dtype, new_dtype) containing the object "
819+
"dtype are not supported");
820+
return -1;
821+
}
822+
775823
/*
776824
* A tuple type would be either (generic typeobject, typesize)
777825
* or (fixed-length data-type, shape)
@@ -809,6 +857,9 @@ _use_inherit(PyArray_Descr *type, PyObject *newobj, int *errflag)
809857
"mismatch in size of old and new data-descriptor");
810858
goto fail;
811859
}
860+
if (new->elsize && invalid_union_object_dtype(new, conv)) {
861+
goto fail;
862+
}
812863
new->elsize = conv->elsize;
813864
if (PyDataType_HASFIELDS(conv)) {
814865
Py_XDECREF(new->fields);
@@ -999,7 +1050,7 @@ _convert_from_dict(PyObject *obj, int align)
9991050
|| (offsets && (n > PyObject_Length(offsets)))
10001051
|| (titles && (n > PyObject_Length(titles)))) {
10011052
PyErr_SetString(PyExc_ValueError,
1002-
"'names', 'formats', 'offsets', and 'titles' dicct "
1053+
"'names', 'formats', 'offsets', and 'titles' dict "
10031054
"entries must have the same length");
10041055
goto fail;
10051056
}

numpy/core/tests/test_regression.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,6 +2214,27 @@ def test_reshape_size_overflow(self):
22142214
new_shape = (2, 7, 7, 43826197)
22152215
assert_raises(ValueError, a.reshape, new_shape)
22162216

2217+
def test_invalid_structured_dtypes(self):
2218+
# gh-2865
2219+
# mapping python objects to other dtypes
2220+
assert_raises(ValueError, np.dtype, ('O', [('name', 'i8')]))
2221+
assert_raises(ValueError, np.dtype, ('i8', [('name', 'O')]))
2222+
assert_raises(ValueError, np.dtype,
2223+
('i8', [('name', [('name', 'O')])]))
2224+
assert_raises(ValueError, np.dtype, ([('a', 'i4'), ('b', 'i4')], 'O'))
2225+
assert_raises(ValueError, np.dtype, ('i8', 'O'))
2226+
# wrong number/type of tuple elements in dict
2227+
assert_raises(ValueError, np.dtype,
2228+
('i', {'name': ('i', 0, 'title', 'oops')}))
2229+
assert_raises(ValueError, np.dtype,
2230+
('i', {'name': ('i', 'wrongtype', 'title')}))
2231+
# disallowed as of 1.13
2232+
assert_raises(ValueError, np.dtype,
2233+
([('a', 'O'), ('b', 'O')], [('c', 'O'), ('d', 'O')]))
2234+
# allowed as a special case due to existing use, see gh-2798
2235+
a = np.ones(1, dtype=('O', [('name', 'O')]))
2236+
assert_equal(a[0], 1)
2237+
22172238

22182239
if __name__ == "__main__":
22192240
run_module_suite()

0 commit comments

Comments
 (0)
0