Skip to content

BUG: ufunc.resolve_dtypes segfaults #31734

Closed

Description

@jorenham

I was trying to figure out how to use ufunc.resolve_dtypes. So as per usual, I threw away the manual and instead just tried a couple of guesses.

First I tried passing 1 to np.sin.resolve_dtypes, that didn't work:

In [1]: np.sin.resolve_dtypes(1)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[1], line 1
----> 1 np.sin.resolve_dtypes(1)

TypeError: resolve_dtypes: The dtypes must be a tuple of `ufunc.nargs` length.

The error message seems helpful though; it must be a tuple (of size 2 in this case), so I tried with (1, 1) next:

In [2]: np.sin.resolve_dtypes((1, 1))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[2], line 1
----> 1 np.sin.resolve_dtypes((1, 1))

TypeError: Provided dtype must be a valid NumPy dtype, int, float, complex, or None.

Hmm, that didn't work either...
It mentions it accepts "dtype", I thought I'd try it with int types, i.e. (int, int):

In [3]: np.sin.resolve_dtypes((int, int))
[1]    1121063 segmentation fault (core dumped)  ipython

😶


I now realize that this works as intended with e.g. resolve_dtypes((float, float)).

This segfault clearly shouldn't have happened though; it should've raised a TypeError with a another helpful error message, like it did when I passed (1, 1) and 1.

Activity

  1. ngoldbaum commented on Jun 24, 2026

    @ngoldbaum
    Member

    Some context from lldb:

    Process 74247 stopped
    * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x3e0)
        frame #0: 0x000000010581856c _multiarray_umath.cpython-314t-darwin.so`PyArray_CommonDType(dtype1=0x0000000000000000, dtype2=0x000004d1b0a9a500) at common_dtype.c:58:20 [opt]
       55
       56  	    PyArray_DTypeMeta *common_dtype;
       57
    -> 58  	    common_dtype = NPY_DT_CALL_common_dtype(dtype1, dtype2);
       59  	    if (common_dtype == (PyArray_DTypeMeta *)Py_NotImplemented) {
       60  	        Py_DECREF(common_dtype);
       61  	        common_dtype = NPY_DT_CALL_common_dtype(dtype2, dtype1);
    Target 0: (python3.14) stopped.
    warning: _multiarray_umath.cpython-314t-darwin.so was compiled with optimization - stepping may behave oddly; variables may not be available.
    (lldb) p dtype2
    (PyArray_DTypeMeta *) 0x000004d1b0a9a500
    (lldb) p dtype1
    (PyArray_DTypeMeta *) NULL
    

    So this is a NULL-pointer dereference. The NULL pointer is coming from a few levels up in the stack:

    PyObject *input;
    if (inputs_tup == NULL) {
    input = NULL;
    }
    else {
    input = PyTuple_GET_ITEM(inputs_tup, i);
    }
    PyArray_Descr *new_descr = npy_find_descr_for_scalar(
    input, descr, original_DTypes[i], signature[i]);

    inputs_tup is NULL, so we end up in the branch that sets descr to NULL and this calls PyArray_CommonDType, which doesn't handle NULL input like this.

    @seberg do you happen to remember why you wrote it this way back in 1cb4044?

  2. seberg commented on Jun 24, 2026

    @seberg
    Member

    Ok, starting at it a bit too long: I think the code is a bit weird but correct. The NULL there is OK and not the NULL that is tripping things.

    The problem is that original_DTypes[i] == NULL here. The interaction there is a bit wild, but does come down a bit to the fact that input makes no sense at all: You cannot have a Python int/float/complex as an output to a ufunc! This works fine:

    np.sin.resolve_dtypes((int, None))
    
  3. MaanasArora commented on Jul 2, 2026

    @MaanasArora
    Contributor

    Just took a look here, and still wrapping my head around it, but yes: I think during the ufunc promotion step the output dtype (which is then original_DTypes[i]) is set to NULL but the incoming Python int is still there as an operand.

    It leaks in because the "allow weak scalars branch" does not reject output dtypes:

    https://github.com/Diamagnetic/numpy/blob/06661b0144c9c06ac07d27d96d5984492900010f/numpy/_core/src/umath/ufunc_object.c#L6427-L6436

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions