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.
Parsing an attacker-crafted
.ggufthrough the publicgguf_open()/gguf_get_tensor()path (and the bundledgguf-tools show <file>CLI) triggers an out-of-bounds write plus out-of-bounds reads, confirmed with AddressSanitizer and UBSan on commitfdfafbe. 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 > 8overflowstensor->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->dimisuint64_t dim[GGUF_TENSOR_MAX_DIM](== 8, gguflib.h).ndimis an attacker-controlled uint32 with no validation other than theassert, which compiles out under-DNDEBUG(the typical release build). A.ggufdeclaring a tensor withn_dims > 8writes past the array.gguf-tools show poison.ggufunder an ASan/UBSan release-equivalent (-DNDEBUG) build reports a stack-buffer-overflow WRITE of size 8 andindex 8 out of bounds for type 'uint64_t[8]'at gguflib.c:283.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_keyuses an attackerstr->len(uint64) to computetype = *(ctx->data + off + 8 + str->len)and to read the key name, with no bound againstctx->size. Confirmed as a SIGSEGV viagguf-tools showon a crafted file. The same unboundedn_dimsvalue also drives an out-of-bounds read in the tensor-offset pre-scan.Suggested fix
gguf_get_tensor, replace theassertwith a real check that returns a parse error (notabort) whenndim > GGUF_TENSOR_MAX_DIM.str->len, the per-dimension reads, array lengths) againstctx->sizebefore dereferencingctx->data + offset.num_weights *= *dimto harden the later allocation (CWE-190).Minimal crafted
.ggufPoCs, the harness, and the full ASan/UBSan traces are available on request.