Bound file-declared length prefixes in the model loader (heap overflow)#542
Open
gigioneggiando wants to merge 1 commit into
Open
Bound file-declared length prefixes in the model loader (heap overflow)#542gigioneggiando wants to merge 1 commit into
gigioneggiando wants to merge 1 commit into
Conversation
shard_load() (safetensors header) and read_gguf_string_fp() (GGUF string) both read a uint64 length straight from a downloaded model file and then do xmalloc((size_t)len + 1) followed by fread(len). A crafted length near SIZE_MAX makes (size_t)len + 1 wrap to 0, so xmalloc() returns a 1-byte buffer (xmalloc maps 0 to malloc(1)); the subsequent fread() of the file's real bytes then overflows it -- a heap-buffer-overflow write. A large but non-wrapping length instead requests a multi-GB allocation from a tiny file. Add read_checked_len_fp(), which reads the length and rejects it if it exceeds the bytes actually remaining in the file, and use it at both sites. Valid lengths (<= file size, so no size_t wrap) are unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two sites in the model-file loader read a
uint64length straight from a downloaded file and then allocate + read with no bound against the file:shard_load()— the safetensors header length;read_gguf_string_fp()— a GGUF string length.Both do
xmalloc((size_t)len + 1)thenfread(len). A crafted length nearSIZE_MAXmakes(size_t)len + 1wrap to 0, soxmalloc()hands back a 1-byte buffer (xmallocmaps 0 tomalloc(1)); the followingfread()of the file's real bytes then overflows it — a heap-buffer-overflow write. A large-but-non-wrapping length instead requests a multi-GB allocation from a tiny file (DoS).These parse files people download and convert (quantized models shared online), so the input is attacker-influenced.
Fix
Add
read_checked_len_fp(), which reads the length prefix and rejects it if it exceeds the bytes actually remaining in the file, and use it at both sites. Valid lengths (<= file size, so nosize_twrap) are unchanged; the file position is restored after the size check. Usesftello/fseeko, already used elsewhere in this file — no new includes.Verification
Built the real
shard_load/read_gguf_string_fpunder ASan and drove crafted files withlen = SIZE_MAX:AddressSanitizer: heap-buffer-overflow WRITE(shard_loadalloc→fread, and the GGUF-string sibling).error: safetensors header length (…) exceeds 64 bytes remaining in file, no sanitizer error.Found during a coordinated security review of ds4; standalone offline PoCs available on request.