Loading a crafted GGUF file makes the loader read outside the mapped file. Filing this publicly as a robustness issue, as suggested when the advisory was closed.
The vendored gguflib builds a tensor's data pointer from fields read straight out of the file. In gguf_get_tensor():
tensor->offset = ctx->data_off + *offset;
tensor->weights_data = ctx->data + tensor->offset;
*offset and bsize come from the file and are never checked against ctx->size. A crafted tensor places weights_data outside the mapping, and any later read of bsize bytes reads out of bounds. MLX reaches this through extract_tensor_data.
Control versus crafted under AddressSanitizer, only the offset field differs:
- offset 0: loads normally, exits clean
- offset 1<<40: SEGV inside
__asan_memcpy on the tensor read
Re-checked on main on 2026-07-17, after #3436 force-enabled the gguflib asserts in release builds. Those asserts cover the metadata and tensor-info walk. They do not cover the data region.
Related but not the same: CVE-2025-62609 added a weights_data == nullptr check in load_gguf(). That does not catch a non-null pointer landing outside the mapping.
Upstream, antirez/gguf-tools#33 proposes the bound, checking offset plus size against the mapping. An earlier PR there, #28, bounded the metadata walk only.
Happy to share the crafted file.
Loading a crafted GGUF file makes the loader read outside the mapped file. Filing this publicly as a robustness issue, as suggested when the advisory was closed.
The vendored gguflib builds a tensor's data pointer from fields read straight out of the file. In
gguf_get_tensor():*offsetandbsizecome from the file and are never checked againstctx->size. A crafted tensor placesweights_dataoutside the mapping, and any later read ofbsizebytes reads out of bounds. MLX reaches this throughextract_tensor_data.Control versus crafted under AddressSanitizer, only the offset field differs:
__asan_memcpyon the tensor readRe-checked on main on 2026-07-17, after #3436 force-enabled the gguflib asserts in release builds. Those asserts cover the metadata and tensor-info walk. They do not cover the data region.
Related but not the same: CVE-2025-62609 added a
weights_data == nullptrcheck inload_gguf(). That does not catch a non-null pointer landing outside the mapping.Upstream, antirez/gguf-tools#33 proposes the bound, checking offset plus size against the mapping. An earlier PR there, #28, bounded the metadata walk only.
Happy to share the crafted file.