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';
   }
   /*!

Reply via email to