IntelPython / dpctl

Python SYCL bindings and SYCL-based Python Array API library
https://intelpython.github.io/dpctl/
Apache License 2.0
99 stars 29 forks source link

segfault in calling __array_namespace__ on a usm_ndarray class #1632

Open icfaust opened 6 months ago

icfaust commented 6 months ago

Python 3.10 (venv) Ubuntu 22.04.3 LTS dpctl installed via pip (0.15.0)

Here is the example which seg faults:

>>> import dpctl
>>> import dpctl.tensor as dpt
>>> import numpy as np
>>> j = dpt.asarray(np.zeros((100,)))
>>> import dpctl
>>> dpctl.__version__
'0.15.0+42.g2a2c98f1f'
>>> type(j).__array_namespace__()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __array_namespace__() needs an argument
>>> type(j).__array_namespace__(type(j))
Segmentation fault (core dumped)

Its a dumb thing to query, but the fact it seg faults is not good. This likely needs to be guarded against.

ndgrigorian commented 6 months ago
In [1]: import dpctl.tensor as dpt, dpctl, numpy as np

In [2]: x = dpt.arange(10)

In [3]: type(x).__dlpack__(type(x))
Segmentation fault

Same seg fault seems to occur with some other properties as well

oleksandr-pavlyk commented 6 months ago

This is rather generic issue with Cython. Define a PXD and PYX files

# a.pxd
cdef class A:
    cdef object v
# a.pyx
# distutils: language = c++
# cython: language_level=3
# cython: linetrace=True

cdef class A:
    """Dummpy class to reproduce crash"""
    def __cinit__(self, val=None):
        self.v = val

    def __value__(self):
        "Get the value"
        return self.v

Cythonize and compile it:

cython --cplus -3 a.pyx
gcc --shared -fPIC a.cpp $(python3-config --includes) -o a.so

Now execute this script:

In [1]: import a

In [2]: inst = a.A("instance")

In [3]: inst.__value__()
Out[3]: 'instance'

In [4]: type(inst).__value__(type(inst))
Segmentation fault (core dumped)

The issue is that type(inst).__value__(type(inst)) is executing method call for uninitialized instance of the class (perhaps the class itself), in which the member .v is uninitialized, or static initialized to null. Remembering that object is just a pointer, dereferencing an uninitialized pointer causes a crash.

This is confirmed by GDB stack:

>>> import a
>>> inst = a.A('instance')
>>> type(inst).__value__(type(inst))

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ba9ce5 in _Py_INCREF (op=0x0) at /localdisk/work/opavlyk/miniconda3/envs/dev_dpctl/include/python3.10/object.h:472
472         op->ob_refcnt++;
(gdb) bt
#0  0x00007ffff7ba9ce5 in _Py_INCREF (op=0x0) at /localdisk/work/opavlyk/miniconda3/envs/dev_dpctl/include/python3.10/object.h:472
#1  0x00007ffff7baa8cf in __pyx_pf_1a_1A_2__value__ (__pyx_v_self=0x7ffff7bb9460 <__pyx_type_1a_A>) at a.cpp:2817
#2  0x00007ffff7baa771 in __pyx_pw_1a_1A_3__value__ (__pyx_v_self=0x7ffff7bb9460 <__pyx_type_1a_A>, __pyx_args=0x7ffff75be0b8, __pyx_nargs=0, __pyx_kwds=0x0) at a.cpp:2792
#3  0x00007ffff7bb23df in __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS (func=0x7ffff7573d30, args=0x7ffff75be0b8, nargsf=9223372036854775809, kwnames=0x0) at a.cpp:6511
oleksandr-pavlyk commented 4 months ago

Filed https://github.com/cython/cython/issues/6207 based on small reproducer above.