diff --git a/cpp/src/arrow/python/numpy_to_arrow.cc b/cpp/src/arrow/python/numpy_to_arrow.cc index a180e3a675ad0..2727ce32f4494 100644 --- a/cpp/src/arrow/python/numpy_to_arrow.cc +++ b/cpp/src/arrow/python/numpy_to_arrow.cc @@ -404,8 +404,13 @@ class NumPyStridedConverter { ARROW_ASSIGN_OR_RAISE(buffer_, AllocateBuffer(sizeof(T) * length_, pool_)); const int64_t stride = PyArray_STRIDES(arr)[0]; - if (stride % sizeof(T) == 0) { - const int64_t stride_elements = stride / sizeof(T); + // ARROW-16013: convert sizeof(T) to signed int64 first, otherwise dividing by it + // would do an unsigned division. This cannot be caught by tests without ubsan, since + // common signed overflow behavior and the fact that the sizeof(T) is currently always + // a power of two here cause CopyStridedNatural to still produce correct results + const int64_t element_size = sizeof(T); + if (stride % element_size == 0) { + const int64_t stride_elements = stride / element_size; CopyStridedNatural(reinterpret_cast(PyArray_DATA(arr)), length_, stride_elements, reinterpret_cast(buffer_->mutable_data())); } else {