Skip to content

Out-of-bounds write in gguf_get_tensor when n_dims > 8 (CWE-787), plus OOB reads in metadata parsing #25

Description

@ValheruEldarr

Parsing an attacker-crafted .gguf through the public gguf_open() / gguf_get_tensor() path (and the bundled gguf-tools show <file> CLI) triggers an out-of-bounds write plus out-of-bounds reads, confirmed with AddressSanitizer and UBSan on commit fdfafbe. These are reported as memory corruption (OOB write) and OOB reads; I do not claim RCE (no control of a saved return address or other hijack primitive was demonstrated).

1. Out-of-bounds write: n_dims > 8 overflows tensor->dim[8] (CWE-787)

gguf_get_tensor (gguflib.c ~272-285) reads the tensor dimension count straight from the file and writes that many dimensions into a fixed-size array:

tensor->ndim = *num_dim;                       // attacker-controlled uint32 from the file
assert(tensor->ndim <= GGUF_TENSOR_MAX_DIM);   // GGUF_TENSOR_MAX_DIM == 8
for (uint32_t j = 0; j < tensor->ndim; j++) {
    ...
    tensor->dim[j] = *dim;                      // dim[8] fixed array -> OOB write once j >= 8
    tensor->num_weights *= *dim;
}

tensor->dim is uint64_t dim[GGUF_TENSOR_MAX_DIM] (== 8, gguflib.h). ndim is an attacker-controlled uint32 with no validation other than the assert, which compiles out under -DNDEBUG (the typical release build). A .gguf declaring a tensor with n_dims > 8 writes past the array.

  • Confirmed: gguf-tools show poison.gguf under an ASan/UBSan release-equivalent (-DNDEBUG) build reports a stack-buffer-overflow WRITE of size 8 and index 8 out of bounds for type 'uint64_t[8]' at gguflib.c:283.
  • With asserts enabled it is an abort() (denial of service) instead of a write, so even the debug build rejects the file only by crashing, not by validating it.

2. Out-of-bounds reads in metadata parsing (CWE-125)

gguf_get_key uses an attacker str->len (uint64) to compute type = *(ctx->data + off + 8 + str->len) and to read the key name, with no bound against ctx->size. Confirmed as a SIGSEGV via gguf-tools show on a crafted file. The same unbounded n_dims value also drives an out-of-bounds read in the tensor-offset pre-scan.

Suggested fix

  • In gguf_get_tensor, replace the assert with a real check that returns a parse error (not abort) when ndim > GGUF_TENSOR_MAX_DIM.
  • Bound every in-file length/offset (str->len, the per-dimension reads, array lengths) against ctx->size before dereferencing ctx->data + offset.
  • Use an overflow-checked multiply for num_weights *= *dim to harden the later allocation (CWE-190).

Minimal crafted .gguf PoCs, the harness, and the full ASan/UBSan traces are available on request.

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