This is an automated email from the ASF dual-hosted git repository.
tqchen 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 b508698 Fix two issues that undefined behavior sanitizer caught (#419)
b508698 is described below
commit b50869806f26d57bf84f52d2d532c82b02d60b1b
Author: Henry Tsang <[email protected]>
AuthorDate: Fri Jan 30 07:31:39 2026 -0800
Fix two issues that undefined behavior sanitizer caught (#419)
Fixes https://github.com/apache/tvm-ffi/issues/413
### Changes
1. **string.h**: Guard `std::memcpy` call with size check to avoid
passing null source pointer with size 0
2. **object.h**: Replace null pointer member access with standard
`offsetof` macro for computing object header offsets
I tested manually for clang and gcc. Didn't test locally for msvc since
I don't have access to windows. CI should test that.
script:
```
#include <tvm/ffi/string.h>
#include <tvm/ffi/object.h>
#include <iostream>
class TestObj : public tvm::ffi::Object {
public:
static constexpr const char* _type_key = "test.TestObj";
};
int main() {
std::cout << "Test 1: Creating string from null TVMFFIByteArray...\n";
TVMFFIByteArray null_bytes{nullptr, 0};
tvm::ffi::String str_from_null(null_bytes);
std::cout << "String size: " << str_from_null.size() << "\n";
std::cout << "Test 2: Computing object offset...\n";
int64_t offset =
tvm::ffi::details::ObjectUnsafe::GetObjectOffsetToSubclass<TestObj>();
std::cout << "Offset: " << offset << "\n";
std::cout << "Done - no UBSan errors!\n";
return 0;
}
```
commands:
```
cd ~/tvm-ffi
git submodule update --init --recursive
clang++ -std=c++17 -fsanitize=undefined -I./include
-I./3rdparty/dlpack/include -x c++ repro.cpp -o repro
g++ -std=c++17 -fsanitize=undefined -I./include -I./3rdparty/dlpack/include
repro.cpp -o repro
```
---
include/tvm/ffi/object.h | 13 +++++++++++--
include/tvm/ffi/string.h | 8 ++++++--
2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/include/tvm/ffi/object.h b/include/tvm/ffi/object.h
index 1f0fe4c..b15363e 100644
--- a/include/tvm/ffi/object.h
+++ b/include/tvm/ffi/object.h
@@ -1092,11 +1092,20 @@ struct ObjectUnsafe {
return const_cast<TVMFFIObject*>(&(src->header_));
}
+// Suppress -Winvalid-offsetof: we intentionally use offsetof on
non-standard-layout types
+// to avoid undefined behavior from null pointer arithmetic that sanitizers
flag.
+#if defined(__clang__) || defined(__GNUC__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Winvalid-offsetof"
+#endif
template <typename Class>
TVM_FFI_INLINE static int64_t GetObjectOffsetToSubclass() {
- return
(reinterpret_cast<int64_t>(&(static_cast<Class*>(nullptr)->header_)) -
-
reinterpret_cast<int64_t>(&(static_cast<Object*>(nullptr)->header_)));
+ return static_cast<int64_t>(__builtin_offsetof(Class, header_)) -
+ static_cast<int64_t>(__builtin_offsetof(Object, header_));
}
+#if defined(__clang__) || defined(__GNUC__)
+#pragma GCC diagnostic pop
+#endif
template <typename T>
TVM_FFI_INLINE static T ObjectRefFromObjectPtr(const ObjectPtr<Object>& ptr)
{
diff --git a/include/tvm/ffi/string.h b/include/tvm/ffi/string.h
index 9c1057a..0c885e9 100644
--- a/include/tvm/ffi/string.h
+++ b/include/tvm/ffi/string.h
@@ -385,7 +385,9 @@ class Bytes {
}
void InitData(const char* data, size_t size) {
char* dest_data = InitSpaceForSize(size);
- std::memcpy(dest_data, data, size);
+ if (size > 0) {
+ std::memcpy(dest_data, data, size);
+ }
// mainly to be compat with string
dest_data[size] = '\0';
}
@@ -754,7 +756,9 @@ class String {
}
void InitData(const char* data, size_t size) {
char* dest_data = InitSpaceForSize(size);
- std::memcpy(dest_data, data, size);
+ if (size > 0) {
+ std::memcpy(dest_data, data, size);
+ }
dest_data[size] = '\0';
}
/*!