mirror of
https://github.com/anchore/syft.git
synced 2026-11-03 16:06:36 +01:00
fix(ai): handle malformed GGUF headers without panicking (#5345)
- pre-scan the GGUF metadata KV section and reject values gguf-parser-go would panic on, recurse on without bound, or overallocate for: wrong typed well-known keys, zero alignment, nested or oversized fully decoded arrays, an unreasonable `split.tensors.count`, and empty strings the parser would misread. Keys are matched the way the parser sees them. The KV count, fully read string bytes, and skipped string counts are bounded too. The file becomes an unknown instead of taking down the scan. - `copyHeader` now bounds its own read to the max header size. - non-finite floats in `header` (including inside arrays) become `"NaN"`, `"+Inf"` or `"-Inf"` so JSON encoding can't fail. `header` and `metadataHash` are otherwise unchanged. - a `metadataHash` failure is now an error instead of an empty hash. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
@@ -72,8 +72,32 @@ func (b *testGGUFBuilder) withUint32KV(key string, value uint32) *testGGUFBuilde
|
||||
return b
|
||||
}
|
||||
|
||||
// withRawKV adds a KV whose value bytes are written verbatim, for arrays and malformed values.
|
||||
func (b *testGGUFBuilder) withRawKV(key string, valueType uint32, raw []byte) *testGGUFBuilder {
|
||||
b.kvPairs = append(b.kvPairs, testKVPair{key: key, valueType: valueType, value: raw})
|
||||
return b
|
||||
}
|
||||
|
||||
// ggufArray encodes an array value: item type, length, then the already encoded items.
|
||||
func ggufArray(itemType uint32, n uint64, items []byte) []byte {
|
||||
buf := new(bytes.Buffer)
|
||||
binary.Write(buf, binary.LittleEndian, itemType)
|
||||
binary.Write(buf, binary.LittleEndian, n)
|
||||
buf.Write(items)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// writeLen writes a count or length, which is 32-bit in GGUF v1 and 64-bit after.
|
||||
func (b *testGGUFBuilder) writeLen(n uint64) {
|
||||
if b.version <= 1 {
|
||||
binary.Write(b.buf, binary.LittleEndian, uint32(n))
|
||||
return
|
||||
}
|
||||
binary.Write(b.buf, binary.LittleEndian, n)
|
||||
}
|
||||
|
||||
func (b *testGGUFBuilder) writeString(s string) {
|
||||
binary.Write(b.buf, binary.LittleEndian, uint64(len(s)))
|
||||
b.writeLen(uint64(len(s)))
|
||||
b.buf.WriteString(s)
|
||||
}
|
||||
|
||||
@@ -85,10 +109,10 @@ func (b *testGGUFBuilder) build() []byte {
|
||||
binary.Write(b.buf, binary.LittleEndian, b.version)
|
||||
|
||||
// Write tensor count
|
||||
binary.Write(b.buf, binary.LittleEndian, b.tensorCount)
|
||||
b.writeLen(b.tensorCount)
|
||||
|
||||
// Write KV count
|
||||
binary.Write(b.buf, binary.LittleEndian, uint64(len(b.kvPairs)))
|
||||
b.writeLen(uint64(len(b.kvPairs)))
|
||||
|
||||
// Write KV pairs
|
||||
for _, kv := range b.kvPairs {
|
||||
@@ -97,6 +121,10 @@ func (b *testGGUFBuilder) build() []byte {
|
||||
// Write value type
|
||||
binary.Write(b.buf, binary.LittleEndian, kv.valueType)
|
||||
// Write value based on type
|
||||
if raw, ok := kv.value.([]byte); ok {
|
||||
b.buf.Write(raw)
|
||||
continue
|
||||
}
|
||||
switch kv.valueType {
|
||||
case ggufTypeString:
|
||||
b.writeString(kv.value.(string))
|
||||
|
||||
Reference in New Issue
Block a user