From 7819364b563ede5091c3f7d803d1684a15b41f40 Mon Sep 17 00:00:00 2001 From: Riku Sakamoto Date: Fri, 10 Oct 2025 14:28:11 +0900 Subject: [PATCH 1/2] BUG: Avoid segfault when calling `numpy._core.strings._expandtabs_length.reduce` Fixes the first item of numpy#28829. Use `Py_XINCREF` instead of `Py_INCREF` to safely handle the case where `op_dtypes[0]` is NULL --- numpy/_core/src/umath/string_ufuncs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numpy/_core/src/umath/string_ufuncs.cpp b/numpy/_core/src/umath/string_ufuncs.cpp index 95f30ccb109e..9b3d86c25301 100644 --- a/numpy/_core/src/umath/string_ufuncs.cpp +++ b/numpy/_core/src/umath/string_ufuncs.cpp @@ -941,7 +941,7 @@ string_expandtabs_length_promoter(PyObject *NPY_UNUSED(ufunc), PyArray_DTypeMeta *const op_dtypes[], PyArray_DTypeMeta *const signature[], PyArray_DTypeMeta *new_op_dtypes[]) { - Py_INCREF(op_dtypes[0]); + Py_XINCREF(op_dtypes[0]); new_op_dtypes[0] = op_dtypes[0]; new_op_dtypes[1] = NPY_DT_NewRef(&PyArray_Int64DType); new_op_dtypes[2] = PyArray_DTypeFromTypeNum(NPY_DEFAULT_INT); From f12325305d78f34309bc8bde083a875dd8690c5f Mon Sep 17 00:00:00 2001 From: Riku Sakamoto Date: Fri, 10 Oct 2025 16:05:50 +0900 Subject: [PATCH 2/2] TST: add tests to check `numpy._core.strings._expandtabs_length` --- numpy/_core/tests/test_strings.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/numpy/_core/tests/test_strings.py b/numpy/_core/tests/test_strings.py index e29151adfffe..d4dbdc921014 100644 --- a/numpy/_core/tests/test_strings.py +++ b/numpy/_core/tests/test_strings.py @@ -4,6 +4,7 @@ import pytest import numpy as np +from numpy._core._exceptions import _UFuncNoLoopError from numpy.testing import IS_PYPY, assert_array_equal, assert_raises from numpy.testing._private.utils import requires_memory @@ -821,6 +822,20 @@ def test_expandtabs_raises_overflow(self, dt): np.strings.expandtabs(np.array("\ta\n\tb", dtype=dt), sys.maxsize) np.strings.expandtabs(np.array("\ta\n\tb", dtype=dt), 2**61) + def test_expandtabs_length_not_cause_segfault(self, dt): + # see gh-28829 + with pytest.raises( + _UFuncNoLoopError, + match="did not contain a loop with signature matching types", + ): + np._core.strings._expandtabs_length.reduce(np.zeros(200)) + + with pytest.raises( + _UFuncNoLoopError, + match="did not contain a loop with signature matching types", + ): + np.strings.expandtabs(np.zeros(200)) + FILL_ERROR = "The fill character must be exactly one character long" def test_center_raises_multiple_character_fill(self, dt):