Skip to content
This repository

segfault in _IsWriteable when data is not owned #481

Open
wence- opened this Issue · 0 comments

1 participant

Lawrence Mitchell
Lawrence Mitchell

The following, albeit slightly contrived, example causes a segfault in _IsWriteable.

foo.pyx:

from libc.stdlib cimport malloc
import numpy as np
cimport numpy as np

np.import_array()

def test(n):

    cdef np.npy_intp _n = n
    cdef void * data

    data = <void *>malloc(_n * sizeof(double))

    return np.PyArray_SimpleNewFromData(1, &_n, np.NPY_DOUBLE, data)

test.py:

import pyximport
pyximport.install()
import foo

a = foo.test(10)
b = a.reshape((5,2))
b.setflags(write=True)

python test.py -> segmentation fault

Here's _IsWriteable (with unnecessary bits removed).

NPY_NO_EXPORT npy_bool
_IsWriteable(PyArrayObject *ap)
{
    PyObject *base=PyArray_BASE(ap);
....
    /* If we own our own data, then no-problem */
    if ((base == NULL) || (PyArray_FLAGS(ap) & NPY_ARRAY_OWNDATA)) {
        return NPY_TRUE;
    }
 ....
    while(PyArray_Check(base)) {
        if (PyArray_CHKFLAGS((PyArrayObject *)base, NPY_ARRAY_OWNDATA)) {
            return (npy_bool) (PyArray_ISWRITEABLE((PyArrayObject *)base));
        }
        base = PyArray_BASE((PyArrayObject *)base);
    }

...
}

a.setflags(write=True) works because

ap == a
base == NULL

and we hit the first condition (base is NULL or we own the data)

b.setflags(write=True) doesn't work because
ap == b
base == a
b.flags['OWNDATA'] == False

so we go into the loop:
base doesn't own the data either, so we try and find the base of a, which is NULL, and PyArray_Check(NULL) is a segfault.

I think the fix is

diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c
index 7b8177c..60a58f6 100644
--- a/numpy/core/src/multiarray/common.c
+++ b/numpy/core/src/multiarray/common.c
@@ -628,6 +628,9 @@ _IsWriteable(PyArrayObject *ap)
             return (npy_bool) (PyArray_ISWRITEABLE((PyArrayObject *)base));
         }
         base = PyArray_BASE((PyArrayObject *)base);
+        if (!base) {
+            return NPY_TRUE;
+        }
     }

     /*

But I'm not sure which of returning True or False is correct.

Lawrence Mitchell wence- referenced this issue in OP2/PyOP2
Merged

Setflags fix #82

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.