Skip to content

gguf : harden loader against malformed tensor dims and metadata types - #25596

Merged
ggerganov merged 3 commits into
ggml-org:masterfrom
harrison001:gguf-harden-malformed-metadata
Aug 12, 2026
Merged

gguf : harden loader against malformed tensor dims and metadata types#25596
ggerganov merged 3 commits into
ggml-org:masterfrom
harrison001:gguf-harden-malformed-metadata

Conversation

@harrison001

@harrison001 harrison001 commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Overview

Hardens the GGUF loader (gguf_init_from_buffer / gguf_init_from_reader) against two crashes reachable from a crafted, untrusted .gguf file. The loader is the trust boundary for any app that loads user-supplied GGUF (e.g. llama-server and downstream tools), so a malformed model file should fail cleanly rather than crash the process.

Additional information

Both issues were found while fuzzing gguf_init_from_buffer with libFuzzer + AddressSanitizer on current master.

1. SIGFPE (division by zero) on a zero tensor dimension

Dimensions are validated as ne[j] >= 0, but the representability check then divides by ne[1..3]:

if (ok && ((INT64_MAX/info.t.ne[1] <= info.t.ne[0]) || ...

A tensor with n_dims >= 2 and ne[1] == 0 (allowed by the >= 0 check) triggers INT64_MAX / 0SIGFPE.

AddressSanitizer: FPE ... gguf.cpp in gguf_init_from_reader(...)

Minimal PoC (65 bytes, base64):
R0dVRgMAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAB0AgAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=

2. Assertion abort on wrong-typed general.alignment

The alignment key is read as uint32 without checking its declared type; a file declaring general.alignment with any non-UINT32 type hits GGML_ASSERT(type_to_gguf_type<T>::value == type)ggml_abort().

gguf.cpp: GGML_ASSERT(type_to_gguf_type<T>::value == type) failed

Minimal PoC (62 bytes, base64):
R0dVRgMAAAAAAAAAAAAAAAEAAAAAAAAAEQAAAAAAAABnZW5lcmFsLmFsaWdubWVudAgAAAABAAAAAAAAAHg=

Fix

  • Skip the overflow check when any ne[j] == 0 (element count is 0, trivially representable).
  • Validate that general.alignment is GGUF_TYPE_UINT32 before reading it; otherwise fail cleanly.

Reproduce: printf '<base64>' | base64 -d > poc.gguf, then load it through any gguf_init_from_* path.

Requirements

  • I have read and agree with the contributing guidelines
  • AI usage disclosure: YES — the two issues were found via libFuzzer + AddressSanitizer fuzzing of gguf_init_from_buffer on master. The fix and the minimal PoCs were drafted with the help of an AI coding assistant and reviewed/verified by me. I am responsible for all submitted changes. Happy to adjust the approach if maintainers prefer different checks.

@github-actions github-actions Bot added the ggml changes relating to the ggml tensor library for machine learning label Jul 12, 2026
@ggml-gh-bot

This comment was marked as resolved.

@harrison001

Copy link
Copy Markdown
Contributor Author

Disclosure: this issue was found via libFuzzer + AddressSanitizer fuzzing of gguf_init_from_buffer on current master. The fix and the minimal PoCs were drafted with the help of an AI coding assistant and reviewed/verified by me. Happy to adjust the approach if maintainers prefer a different check.

@harrison001 harrison001 mentioned this pull request Jul 12, 2026

@JohannesGaessler JohannesGaessler left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add corresponding test cases to tests/test-gguf.cpp.

Comment thread ggml/src/gguf.cpp Outdated
Comment thread ggml/src/gguf.cpp Outdated
- report the expected vs. actual type when general.alignment is not u32
- use ggml_nelements() > 0 for the zero-element guard and keep the
  representability checks visually aligned
- add test-gguf cases for a wrong-typed alignment key and a zero-dim
  tensor (both used to crash: assert-abort and SIGFPE respectively)

Ran tests/test-gguf: 164/164 pass. Used an AI assistant to help draft
these edits; reviewed and verified by me.
@harrison001
harrison001 requested a review from ggerganov as a code owner July 13, 2026 19:55
@github-actions github-actions Bot added the testing Everything test related label Jul 13, 2026
@harrison001

Copy link
Copy Markdown
Contributor Author

Please add corresponding test cases to tests/test-gguf.cpp.

Added test cases in 3cd5206: two handcrafted files in tests/test-gguf.cpp — a general.alignment key with a wrong (non-u32) type, and a tensor with a zero-size dimension. Both used to crash before (assert-abort and SIGFPE); with the fix they load cleanly. Full suite passes (164/164).

@end2benzing

Copy link
Copy Markdown

I ran an unrelated fuzzing campaign against gguf_init_from_buffer
on current master (libFuzzer + ASan/UBSan, ~400k iterations) and hit the same SIGFPE at
gguf.cpp:685 via a zero tensor dimension (ne[1] == 0 passing the >= 0 check, then
INT64_MAX / 0). Confirms this reproduces reliably and isn't a one-off.

Also did a manual pass on the surrounding overflow check — the ne[0]/ne[2]/ne[3]
multiplications can't independently overflow due to the || short-circuit, so the zero-check
in this PR looks like it fully covers the defect rather than being a partial fix.

found via an AI-assisted fuzzing/static-analysis pipeline I built and ran myself,
I reviewed and verified the crash and the analysis above personally.

No new PoC needed since this PR's reproducer already covers it — just flagging that it's a
live, currently-exploitable-by-anyone-loading-untrusted-models bug on master, not theoretical,
in case that helps with review priority.

@CISC

CISC commented Aug 12, 2026

Copy link
Copy Markdown
Member

LGTM @ggerganov?

@ggerganov ggerganov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems OK.

My memory is a bit fuzzy on the topic of zero-sized tensors (i.e. ne[k] == 0). There is explicit logic there to allow them, but at the same time, I don't think there is any purpose to do so - these are basically empty tensors.

@Green-Sky

Copy link
Copy Markdown
Collaborator

ggml-org/ggml#1517 (comment) They are to be allowed.

safetensors allows them and they are used in some models (diffusion stuff) to tag it for a specific feature, instead of kv.

Comment thread tests/test-gguf.cpp
Comment thread tests/test-gguf.cpp
Comment thread tests/test-gguf.cpp Outdated
Co-authored-by: Georgi Gerganov <ggerganov@gmail.com>
@ggerganov ggerganov added the merge ready A maintainer can use this label to indicate that they consider the changes final and ready to merge. label Aug 12, 2026
@ggerganov
ggerganov merged commit d8a8bea into ggml-org:master Aug 12, 2026
25 of 28 checks passed
gabe-l-hart added a commit to gabe-l-hart/llama.cpp that referenced this pull request Aug 12, 2026
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>

* origin/master: (383 commits)
  cmake :  introduce semantic versioning  (ggml-org#26839)
  gguf : harden loader against malformed tensor dims and metadata types (ggml-org#25596)
  kleidiai: Add runtime feature detection mechanism for aarch64/kleidiai (ggml-org#26076)
  model : disallow integer dflash sliding_window_pattern (ggml-org#26900)
  sync : ggml
  cmake : add config version support (ggml/1582)
  server : support slot save/restore with media inputs (ggml-org#26640)
  ui: add read_media tool (ggml-org#25877)
  opencl: default FA c8 cluster width to 16 on X1E (ggml-org#26433)
  tests : update speculative params (ggml-org#26925)
  vulkan: add TQ2_0 (ternary) support (ggml-org#25850)
  wavtokenizer-dec : bound posnet/convnext block_count against n_layer_all (ggml-org#26892)
  convert : handle per_layer_config in Gemma4 (transformers 5.15) (ggml-org#26882)
  opencl: use flat mv q5_k when weight exceeds image1d_buffer_t limit (ggml-org#26880)
  chat : fix muse-glimmer detection of tool calls after EOM (ggml-org#26879)
  ci : add missing release check (ggml-org#26923)
  CUDA: only disable CUDA graphs when mul_mat_id actually needs a stream sync (ggml-org#26802)
  cuda : add warp-per-row wkv7 kernel for single-token decode (ggml-org#26111)
  spec : update speculative-simple (ggml-org#26904)
  chat : tighten bare function parsing for Qwen models (ggml-org#26793)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ggml changes relating to the ggml tensor library for machine learning merge ready A maintainer can use this label to indicate that they consider the changes final and ready to merge. testing Everything test related

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants