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 86bbddf feat: Add overflow check for uint64_t/size_t in
TypeTraits<Int>::CopyToAnyView (#370)
86bbddf is described below
commit 86bbddfdbaa9103016e3f39633b8b2402ea24428
Author: Nan <[email protected]>
AuthorDate: Thu Jan 8 17:49:37 2026 +0800
feat: Add overflow check for uint64_t/size_t in
TypeTraits<Int>::CopyToAnyView (#370)
Related discussion here #357
Adds runtime overflow detection when converting unsigned 64-bit integers
([uint64_t or
size_t](https://stackoverflow.com/questions/73820966/stdis-same-vsize-t-uint64-t-evaluates-to-false-when-both-types-are-8-by
)) to int64_t in TVM FFI Any.
128-bit signed integers (if exists) are not considered.
---------
Co-authored-by: Junru Shao <[email protected]>
---
include/tvm/ffi/type_traits.h | 8 ++++++++
src/ffi/extra/structural_hash.cc | 24 ++++++++++++++++++------
tests/cpp/test_any.cc | 16 ++++++++++++++++
tests/cpp/testing_object.h | 5 ++---
4 files changed, 44 insertions(+), 9 deletions(-)
diff --git a/include/tvm/ffi/type_traits.h b/include/tvm/ffi/type_traits.h
index 437dcd9..1fd1304 100644
--- a/include/tvm/ffi/type_traits.h
+++ b/include/tvm/ffi/type_traits.h
@@ -28,6 +28,7 @@
#include <tvm/ffi/error.h>
#include <tvm/ffi/object.h>
+#include <limits>
#include <string>
#include <type_traits>
#include <utility>
@@ -279,6 +280,13 @@ struct TypeTraits<Int,
std::enable_if_t<std::is_integral_v<Int>>> : public TypeT
static constexpr int32_t field_static_type_index = TypeIndex::kTVMFFIInt;
TVM_FFI_INLINE static void CopyToAnyView(const Int& src, TVMFFIAny* result) {
+ if constexpr (std::is_unsigned_v<Int> && sizeof(Int) >= sizeof(int64_t)) {
+ if (src > static_cast<Int>(std::numeric_limits<int64_t>::max())) {
+ TVM_FFI_THROW(OverflowError)
+ << "Integer value " << src << " is too large to fit in int64_t. "
+ << "Consider explicitly casting to int64_t first if this is
intentional.";
+ }
+ }
result->type_index = TypeIndex::kTVMFFIInt;
result->zero_padding = 0;
result->v_int64 = static_cast<int64_t>(src);
diff --git a/src/ffi/extra/structural_hash.cc b/src/ffi/extra/structural_hash.cc
index 271a0db..5bb9eb1 100644
--- a/src/ffi/extra/structural_hash.cc
+++ b/src/ffi/extra/structural_hash.cc
@@ -150,15 +150,19 @@ class StructuralHashHandler {
std::swap(allow_free_var, map_free_vars_);
uint64_t hash_value = HashAny(val);
std::swap(allow_free_var, map_free_vars_);
- return details::StableHashCombine(init_hash, hash_value);
+ return
static_cast<int64_t>(details::StableHashCombine(init_hash, hash_value));
} else {
- return details::StableHashCombine(init_hash, HashAny(val));
+ // we explicitly bitcast the result from `uint64_t` to
`int64_t`.
+ // The range of `uint64_t` is too large to fit as `int64_t`,
so if we don't bitcast,
+ // it will trigger an overflow error in `uint64_t` -> `Any`
conversion.
+ return
static_cast<int64_t>(details::StableHashCombine(init_hash, HashAny(val)));
}
});
}
- hash_value = custom_s_hash[type_info->type_index]
- .cast<ffi::Function>()(obj, hash_value,
s_hash_callback_)
- .cast<uint64_t>();
+ hash_value =
+ custom_s_hash[type_info->type_index]
+ .cast<ffi::Function>()(obj, static_cast<int64_t>(hash_value),
s_hash_callback_)
+ .cast<uint64_t>();
}
if (structural_eq_hash_kind == kTVMFFISEqHashKindFreeVar) {
@@ -311,9 +315,17 @@ uint64_t StructuralHash::Hash(const Any& value, bool
map_free_vars, bool skip_te
return handler.HashAny(value);
}
+static int64_t FFIStructuralHash(const Any& value, bool map_free_vars, bool
skip_tensor_content) {
+ uint64_t result = StructuralHash::Hash(value, map_free_vars,
skip_tensor_content);
+ // we explicitly bitcast the result from `uint64_t` to `int64_t`.
+ // The range of `uint64_t` is too large to fit as `int64_t`, so if we don't
bitcast,
+ // it will trigger an overflow error in `uint64_t` -> `Any` conversion.
+ return static_cast<int64_t>(result);
+}
+
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
- refl::GlobalDef().def("ffi.StructuralHash", StructuralHash::Hash);
+ refl::GlobalDef().def("ffi.StructuralHash", FFIStructuralHash);
refl::EnsureTypeAttrColumn("__s_hash__");
}
diff --git a/tests/cpp/test_any.cc b/tests/cpp/test_any.cc
index 55abbbf..c29ad5c 100644
--- a/tests/cpp/test_any.cc
+++ b/tests/cpp/test_any.cc
@@ -20,6 +20,8 @@
#include <tvm/ffi/any.h>
#include <tvm/ffi/memory.h>
+#include <limits>
+
#include "./testing_object.h"
namespace {
@@ -58,6 +60,20 @@ TEST(Any, Int) {
view0 = v1;
EXPECT_EQ(view0.CopyToTVMFFIAny().type_index, TypeIndex::kTVMFFIInt);
EXPECT_EQ(view0.CopyToTVMFFIAny().v_int64, 2);
+
+ uint64_t v2 = static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1;
+ EXPECT_THROW(
+ {
+ try {
+ view0 = v2;
+ } catch (const Error& error) {
+ EXPECT_EQ(error.kind(), "OverflowError");
+ std::string what = error.what();
+ EXPECT_NE(what.find("is too large to fit in int64_t"),
std::string::npos);
+ throw;
+ }
+ },
+ ::tvm::ffi::Error);
}
TEST(Any, Enum) {
diff --git a/tests/cpp/testing_object.h b/tests/cpp/testing_object.h
index 933ba99..318aa11 100644
--- a/tests/cpp/testing_object.h
+++ b/tests/cpp/testing_object.h
@@ -233,9 +233,8 @@ class TCustomFuncObj : public Object {
return true;
}
- uint64_t SHash(uint64_t init_hash,
- ffi::TypedFunction<uint64_t(AnyView, uint64_t, bool)> hash)
const {
- uint64_t hash_value = init_hash;
+ int64_t SHash(int64_t init_hash, ffi::TypedFunction<int64_t(AnyView,
int64_t, bool)> hash) const {
+ int64_t hash_value = init_hash;
hash_value = hash(params, hash_value, true);
hash_value = hash(body, hash_value, false);
return hash_value;