From 60e80dc4078f9d1c49b6767d22b97f456bdaf086 Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Wed, 20 May 2026 05:09:51 -0700 Subject: [PATCH 1/4] Add strides validation to usm_ndarray constructor for new allocations --- dpnp/tensor/_usmarray.pyx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dpnp/tensor/_usmarray.pyx b/dpnp/tensor/_usmarray.pyx index 7d90ffeb05a1..2c2d9becd027 100644 --- a/dpnp/tensor/_usmarray.pyx +++ b/dpnp/tensor/_usmarray.pyx @@ -378,6 +378,20 @@ cdef class usm_ndarray: elif isinstance(buffer, (str, bytes)): if isinstance(buffer, bytes): buffer = buffer.decode("UTF-8") + if strides is not None and ary_min_displacement < 0: + self._cleanup() + raise ValueError( + "strides={} result in a negative memory displacement " + "and are not allowed when allocating new " + "memory".format(strides)) + if strides is not None and ( + (ary_max_displacement - ary_min_displacement + 1) > ary_nelems + ): + self._cleanup() + raise ValueError( + "strides={} is incompatible with shape={} when " + "allocating new memory because the memory footprint " + "exceeds the number of elements".format(strides, shape)) _offset = -ary_min_displacement if (buffer == "shared"): _buffer = dpmem.MemoryUSMShared(ary_nbytes, From 29512748689d4f005e8f554776d22b3b549d288e Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Wed, 20 May 2026 05:10:22 -0700 Subject: [PATCH 2/4] Add test_ctor_invalid_strides --- dpnp/tests/tensor/test_usm_ndarray_ctor.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/dpnp/tests/tensor/test_usm_ndarray_ctor.py b/dpnp/tests/tensor/test_usm_ndarray_ctor.py index b03a01ad370b..0ce5557e2c0f 100644 --- a/dpnp/tests/tensor/test_usm_ndarray_ctor.py +++ b/dpnp/tests/tensor/test_usm_ndarray_ctor.py @@ -123,9 +123,6 @@ def test_usm_ndarray_flags(): f = dpt.usm_ndarray((5, 0, 1), dtype="i4", strides=(1, 0, 1)).flags assert f.fc assert f.forc - assert not dpt.usm_ndarray( - (5, 1, 1), dtype="i4", strides=(2, 0, 1) - ).flags.forc x = dpt.empty(5, dtype="u2") assert x.flags.writable is True @@ -1146,6 +1143,19 @@ def test_ctor_invalid(): dpt.usm_ndarray((4,), dtype="u1", buffer=m, strides={"not": "valid"}) +def test_ctor_invalid_strides(): + try: + dpt.usm_ndarray((1,), dtype="i4") + except dpctl.SyclDeviceCreationError: + pytest.skip("No SYCL devices available") + # negative displacement + with pytest.raises(ValueError): + dpt.usm_ndarray((2, 3, 4), dtype="i4", strides=(-1, 1, 1)) + # oversized memory footprint + with pytest.raises(ValueError): + dpt.usm_ndarray((2, 3, 4), dtype="i4", strides=(1, 16, 128)) + + def test_reshape(): try: X = dpt.usm_ndarray((5, 5), "i4") From cd3a953b65cc901b4410ed4b5f24af9a139f2c9a Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Wed, 20 May 2026 05:20:49 -0700 Subject: [PATCH 3/4] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a7382673462..956055e70e8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ This release is compatible with NumPy 2.4.5. * Fixed incorrect `dpnp.tensor.acosh` result for `complex(±0, NaN)` special case to match the Python Array API specification [#2914](https://github.com/IntelPython/dpnp/pull/2914) * Fixed fork PR documentation workflow failures by implementing conditional publishing strategy: upstream PRs publish to GitHub Pages with comment, fork PRs upload artifacts [#2910](https://github.com/IntelPython/dpnp/pull/2910) * Fixed missing `libtensor` headers in the installed `dpnp` package [#2915](https://github.com/IntelPython/dpnp/pull/2915) +* Fixed missing strides validation in `dpnp.tensor.usm_ndarray` constructor when allocating new memory [#2927](https://github.com/IntelPython/dpnp/pull/2927) ### Security From e714946f8a5edc899d1e7da5e878a189c8331fd7 Mon Sep 17 00:00:00 2001 From: Vladislav Perevezentsev Date: Thu, 23 Jul 2026 06:46:32 -0700 Subject: [PATCH 4/4] Use f-strings in _usmarray.pyx --- dpnp/tensor/_usmarray.pyx | 47 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/dpnp/tensor/_usmarray.pyx b/dpnp/tensor/_usmarray.pyx index 91c5bd5dab42..9e9e98616153 100644 --- a/dpnp/tensor/_usmarray.pyx +++ b/dpnp/tensor/_usmarray.pyx @@ -378,12 +378,11 @@ cdef class usm_ndarray: if (typenum < 0): if typenum == -2: raise ValueError( - "Data type '" + str(dtype) + - "' can only have native byteorder." + f"Data type '{dtype}' can only have native byteorder." ) elif typenum == -1: raise ValueError( - "Data type '" + str(dtype) + "' is not understood." + f"Data type '{dtype}' is not understood." ) raise TypeError( f"Expected string or a dtype object, got {type(dtype)}" @@ -391,7 +390,7 @@ cdef class usm_ndarray: itemsize = type_bytesize(typenum) if (itemsize < 1): raise TypeError( - "dtype=" + np.dtype(dtype).name + " is not supported." + f"dtype={np.dtype(dtype).name} is not supported." ) # allocate host C-arrays for shape, strides err = _from_input_shape_strides( @@ -406,11 +405,11 @@ cdef class usm_ndarray: "array failed.") elif err == ERROR_INCORRECT_ORDER: raise ValueError( - "Unsupported order='{}' given. " - "Supported values are 'C' or 'F'.".format(order)) + f"Unsupported order='{order}' given. " + "Supported values are 'C' or 'F'.") elif err == ERROR_UNEXPECTED_STRIDES: raise ValueError( - "strides={} is not understood".format(strides)) + f"strides={strides} is not understood") else: raise InternalUSMArrayError( " .. while processing shape and strides.") @@ -424,17 +423,18 @@ cdef class usm_ndarray: if strides is not None and ary_min_displacement < 0: self._cleanup() raise ValueError( - "strides={} result in a negative memory displacement " - "and are not allowed when allocating new " - "memory".format(strides)) + f"strides={strides} result in a negative memory " + "displacement and are not allowed when allocating " + "new memory") if strides is not None and ( (ary_max_displacement - ary_min_displacement + 1) > ary_nelems ): self._cleanup() raise ValueError( - "strides={} is incompatible with shape={} when " - "allocating new memory because the memory footprint " - "exceeds the number of elements".format(strides, shape)) + f"strides={strides} is incompatible with " + f"shape={shape} when allocating new memory because " + "the memory footprint exceeds the number of elements" + ) _offset = -ary_min_displacement if (buffer == "shared"): _buffer = dpmem.MemoryUSMShared(ary_nbytes, @@ -448,10 +448,9 @@ cdef class usm_ndarray: else: self._cleanup() raise ValueError( - "buffer='{}' is not understood. " + f"buffer='{buffer}' is not understood. " "Recognized values are 'device', 'shared', 'host', " "an instance of `MemoryUSM*` object, or a usm_ndarray" - "".format(buffer) ) elif isinstance(buffer, usm_ndarray): if not buffer.flags.writable: @@ -459,13 +458,14 @@ cdef class usm_ndarray: _buffer = buffer.usm_data else: self._cleanup() - raise ValueError("buffer='{}' was not understood.".format(buffer)) + raise ValueError(f"buffer='{buffer}' was not understood.") if (shape_to_elem_count(nd, shape_ptr) > 0 and (_offset + ary_min_displacement < 0 or (_offset + ary_max_displacement + 1) * itemsize > _buffer.nbytes)): self._cleanup() - raise ValueError(("buffer='{}' can not accommodate " - "the requested array.").format(buffer)) + raise ValueError( + f"buffer='{buffer}' can not accommodate the requested array." + ) is_fp64 = (typenum == UAR_DOUBLE or typenum == UAR_CDOUBLE) is_fp16 = (typenum == UAR_HALF) if (is_fp64 or is_fp16): @@ -654,9 +654,8 @@ cdef class usm_ndarray: if (not isinstance(self.base_, dpmem._memory._Memory)): raise InternalUSMArrayError( "Invalid instance of usm_ndarray encountered. " - "Private field base_ has an unexpected type {}.".format( - type(self.base_) - ) + "Private field base_ has an unexpected type " + f"{type(self.base_)}." ) ary_iface = self.base_.__sycl_usm_array_interface__ mem_ptr = ( ary_iface["data"][0]) @@ -817,7 +816,7 @@ cdef class usm_ndarray: self.strides_ = strides_ptr else: raise InternalUSMArrayError( - "Encountered in shape setter, error code {err}".format(err) + f"Encountered in shape setter, error code {err}" ) @property @@ -1919,7 +1918,7 @@ cdef api object UsmNDArray_MakeSimpleFromPtr( cdef int itemsize = type_bytesize(typenum) if (itemsize < 1): raise ValueError( - "dtype with typenum=" + str(typenum) + " is not supported." + f"dtype with typenum={typenum} is not supported." ) cdef size_t nbytes = ( itemsize) * nelems cdef c_dpmem._Memory mobj @@ -1975,7 +1974,7 @@ cdef api object UsmNDArray_MakeFromPtr( if (itemsize < 1): raise ValueError( - "dtype with typenum=" + str(typenum) + " is not supported." + f"dtype with typenum={typenum} is not supported." ) if (nd < 0): raise ValueError("Dimensionality must be non-negative")