Skip to content

Signed shape-product wrap in safetensors reader (NULL deref / DoS, ASAN-confirmed) #1876

Description

@x14ngch3n

Summary

read_safetensors_file (src/model_io/safetensors_io.cpp) accepts a crafted .safetensors file whose tensor shape overflows the element-count product to 0, leading to a NULL pointer dereference (denial of service) when the tensor is later accessed.

Root cause

Each tensor dimension is parsed with shape[i].get<int64_t>() and stored into ne[] with no range or overflow check. TensorStorage::nelements() (src/model_io/tensor_storage.h) multiplies the dims as a signed int64 product, and the reader uses nbytes() (derived from that product) for its only size check:

tensor_size_ok = (tensor_storage.nbytes() == tensor_data_size);   // safetensors_io.cpp:346
// tensor_data_size = end - begin, bounded against the file at :255

shape = [4294967296, 4294967296]int64 product 2^32 · 2^32 = 2^64 ≡ 0nbytes() == 0. Paired with an empty in-bounds data range (data_offsets [x, x]), 0 == 0 passes the check and the tensor is accepted with huge ne[] but nbytes() == 0. Negative dimensions are also accepted at parse time and reach the int64 product.

Impact

ggml_new_tensor computes its allocation from the same ne[] and also wraps to 0, returning a tensor with data == NULL. The first consumer access dereferences NULL → crash. A malicious model file causes a denial of service.

Reproduction

import struct, json
m = {"t":{"dtype":"I8","shape":[4294967296,4294967296],"data_offsets":[0,0]}}
j = json.dumps(m, separators=(",",":")).encode()
open("poc.safetensors","wb").write(struct.pack("<Q", len(j)) + j)
# load -> read_safetensors_file ACCEPTS; ggml_new_tensor -> data=NULL -> SEGV on access

Suggested fix

Validate each parsed dimension (reject <= 0) and compute the element-count product with __builtin_mul_overflow, rejecting the tensor up front on a non-positive dim or overflow. PR: #1875.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions