This is an automated email from the ASF dual-hosted git repository.
junrushao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git
The following commit(s) were added to refs/heads/main by this push:
new e54d15d fix: add bounds checking for size() and stride() methods
(#375)
e54d15d is described below
commit e54d15d71c64da72e84cc831def06dc525e31e18
Author: Guan-Ming (Wesley) Chiu <[email protected]>
AuthorDate: Fri Jan 2 02:45:26 2026 +0800
fix: add bounds checking for size() and stride() methods (#375)
## Why
size() and stride() methods could access memory out of bounds when given
invalid indices
## How
- Add bounds checking after adjusting negative indices
- Throw IndexError with descriptive message when index is out of bounds
- Add tests for both Tensor and TensorView classes
---
include/tvm/ffi/container/tensor.h | 32 ++++++++++++++++++++++++++++----
tests/cpp/test_tensor.cc | 14 ++++++++++++++
tests/python/test_tensor.py | 9 +++++----
3 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/include/tvm/ffi/container/tensor.h
b/include/tvm/ffi/container/tensor.h
index 3675bb5..eb6c9fe 100644
--- a/include/tvm/ffi/container/tensor.h
+++ b/include/tvm/ffi/container/tensor.h
@@ -325,7 +325,12 @@ class Tensor : public ObjectRef {
*/
int64_t size(int64_t idx) const {
const TensorObj* ptr = get();
- return ptr->shape[idx >= 0 ? idx : (ptr->ndim + idx)];
+ int64_t adjusted_idx = idx >= 0 ? idx : (ptr->ndim + idx);
+ if (adjusted_idx < 0 || adjusted_idx >= ptr->ndim) {
+ TVM_FFI_THROW(IndexError) << "Index " << idx << " out of bounds for
tensor with " << ptr->ndim
+ << " dimensions";
+ }
+ return ptr->shape[adjusted_idx];
}
/*!
@@ -336,7 +341,12 @@ class Tensor : public ObjectRef {
*/
int64_t stride(int64_t idx) const {
const TensorObj* ptr = get();
- return ptr->strides[idx >= 0 ? idx : (ptr->ndim + idx)];
+ int64_t adjusted_idx = idx >= 0 ? idx : (ptr->ndim + idx);
+ if (adjusted_idx < 0 || adjusted_idx >= ptr->ndim) {
+ TVM_FFI_THROW(IndexError) << "Index " << idx << " out of bounds for
tensor with " << ptr->ndim
+ << " dimensions";
+ }
+ return ptr->strides[adjusted_idx];
}
/*!
@@ -754,7 +764,14 @@ class TensorView {
* \param idx The index of the size.
* \return The size of the idx-th dimension.
*/
- int64_t size(int64_t idx) const { return tensor_.shape[idx >= 0 ? idx :
tensor_.ndim + idx]; }
+ int64_t size(int64_t idx) const {
+ int64_t adjusted_idx = idx >= 0 ? idx : (tensor_.ndim + idx);
+ if (adjusted_idx < 0 || adjusted_idx >= tensor_.ndim) {
+ TVM_FFI_THROW(IndexError) << "Index " << idx << " out of bounds for
tensor with "
+ << tensor_.ndim << " dimensions";
+ }
+ return tensor_.shape[adjusted_idx];
+ }
/*!
* \brief Get the stride of the idx-th dimension. If the idx is negative,
@@ -762,7 +779,14 @@ class TensorView {
* \param idx The index of the stride.
* \return The stride of the idx-th dimension.
*/
- int64_t stride(int64_t idx) const { return tensor_.strides[idx >= 0 ? idx :
tensor_.ndim + idx]; }
+ int64_t stride(int64_t idx) const {
+ int64_t adjusted_idx = idx >= 0 ? idx : (tensor_.ndim + idx);
+ if (adjusted_idx < 0 || adjusted_idx >= tensor_.ndim) {
+ TVM_FFI_THROW(IndexError) << "Index " << idx << " out of bounds for
tensor with "
+ << tensor_.ndim << " dimensions";
+ }
+ return tensor_.strides[adjusted_idx];
+ }
/*!
* \brief Get the byte offset of the Tensor.
diff --git a/tests/cpp/test_tensor.cc b/tests/cpp/test_tensor.cc
index b5c82bc..1b14e6f 100644
--- a/tests/cpp/test_tensor.cc
+++ b/tests/cpp/test_tensor.cc
@@ -365,4 +365,18 @@ TEST(Tensor, AsStrided) {
EXPECT_EQ(offset_data2[0 * 3 + 0 * 1], 2.0f); // Points to data[2]
}
+TEST(Tensor, SizeStrideOutOfBounds) {
+ Tensor tensor = Empty({2, 3, 4}, DLDataType({kDLFloat, 32, 1}),
DLDevice({kDLCPU, 0}));
+ EXPECT_THROW({ tensor.size(3); }, tvm::ffi::Error);
+ EXPECT_THROW({ tensor.size(-4); }, tvm::ffi::Error);
+ EXPECT_THROW({ tensor.stride(3); }, tvm::ffi::Error);
+ EXPECT_THROW({ tensor.stride(-4); }, tvm::ffi::Error);
+
+ TensorView tensor_view = tensor;
+ EXPECT_THROW({ tensor_view.size(3); }, tvm::ffi::Error);
+ EXPECT_THROW({ tensor_view.size(-4); }, tvm::ffi::Error);
+ EXPECT_THROW({ tensor_view.stride(3); }, tvm::ffi::Error);
+ EXPECT_THROW({ tensor_view.stride(-4); }, tvm::ffi::Error);
+}
+
} // namespace
diff --git a/tests/python/test_tensor.py b/tests/python/test_tensor.py
index 9c938a8..d091d85 100644
--- a/tests/python/test_tensor.py
+++ b/tests/python/test_tensor.py
@@ -20,6 +20,7 @@ from __future__ import annotations
from types import ModuleType
from typing import Any, NamedTuple
+import numpy.typing as npt
import pytest
torch: ModuleType | None
@@ -33,7 +34,7 @@ import tvm_ffi
def test_tensor_attributes() -> None:
- data = np.zeros((10, 8, 4, 2), dtype="int16")
+ data: npt.NDArray[Any] = np.zeros((10, 8, 4, 2), dtype="int16")
if not hasattr(data, "__dlpack__"):
return
x = tvm_ffi.from_dlpack(data)
@@ -84,7 +85,7 @@ def test_tensor_class_override() -> None:
old_tensor = tvm_ffi.core._CLASS_TENSOR
tvm_ffi.core._set_class_tensor(MyTensor)
- data = np.zeros((10, 8, 4, 2), dtype="int16")
+ data: npt.NDArray[Any] = np.zeros((10, 8, 4, 2), dtype="int16")
if not hasattr(data, "__dlpack__"):
return
x = tvm_ffi.from_dlpack(data)
@@ -105,7 +106,7 @@ def test_tvm_ffi_tensor_compatible() -> None:
"""Implement __tvm_ffi_object__ protocol."""
return self._tensor
- data = np.zeros((10, 8, 4, 2), dtype="int32")
+ data: npt.NDArray[Any] = np.zeros((10, 8, 4, 2), dtype="int32")
if not hasattr(data, "__dlpack__"):
return
x = tvm_ffi.from_dlpack(data)
@@ -159,7 +160,7 @@ def test_optional_tensor_view() -> None:
"testing.optional_tensor_view_has_value"
)
assert not optional_tensor_view_has_value(None)
- x = np.zeros((128,), dtype="float32")
+ x: npt.NDArray[Any] = np.zeros((128,), dtype="float32")
if not hasattr(x, "__dlpack__"):
return
assert optional_tensor_view_has_value(x)