From f4a09b4b7724194a1a3282c3a6e1ce6e798299d7 Mon Sep 17 00:00:00 2001 From: localai-org-maint-bot Date: Thu, 8 Oct 2026 09:56:59 +0200 Subject: [PATCH] fix(vram): reject oversized GGUF metadata before allocation (#12560) Upgrade gguf-parser-go to v0.26.3 so string lengths are checked against the remaining file size before allocation. Master AIO CI crashed in the background gallery warmer when v0.25.0 tried to allocate several terabytes; panic recovery cannot catch a fatal runtime OOM. Check both overflow-sized and file-exceeding strings through the remote reader, and document the size-only estimate fallback. Assisted-by: Codex:gpt-6 Co-authored-by: localai-org-maint-bot <306269227+localai-org-maint-bot@users.noreply.github.com> --- docs/content/features/model-gallery.md | 2 +- go.mod | 2 +- go.sum | 2 ++ pkg/vram/gguf_reader_test.go | 14 ++++++++++++-- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/docs/content/features/model-gallery.md b/docs/content/features/model-gallery.md index 3bdfc6d41..b665be21c 100644 --- a/docs/content/features/model-gallery.md +++ b/docs/content/features/model-gallery.md @@ -172,7 +172,7 @@ This removal does not delete previously installed models. Remove that configurat When browsing the gallery or importing a model by URI, LocalAI can show **estimated download size** and **estimated VRAM** for models. - **Where they appear**: In the model gallery table (Size / VRAM column), in the model detail modal, and after starting an import from URI (in the success message). -- **How they are computed**: GGUF models use file size (HTTP HEAD or local stat) and optional GGUF metadata (HTTP Range) for KV cache and overhead; other formats use Hugging Face file sizes and optional config when available. If metadata is unavailable, a size-only heuristic is used. +- **How they are computed**: GGUF models use file size (HTTP HEAD or local stat) and optional GGUF metadata (HTTP Range) for KV cache and overhead; other formats use Hugging Face file sizes and optional config when available. If metadata is unavailable, a size-only heuristic is used. GGUF metadata lengths that exceed the file size are rejected before allocation; these files also use the size-only estimate. - **Hardware fit indicator**: When your system reports GPU or RAM capacity, the gallery shows whether the estimated VRAM fits (green) or may not fit (red) using a 95% headroom rule. - Estimates are best-effort and may be missing if the server does not support HEAD/Range or the request times out. diff --git a/go.mod b/go.mod index e16d680f5..d42203dac 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/gofrs/flock v0.13.0 github.com/google/go-containerregistry v0.21.6 github.com/google/uuid v1.6.0 - github.com/gpustack/gguf-parser-go v0.25.0 + github.com/gpustack/gguf-parser-go v0.26.3 github.com/hpcloud/tail v1.0.0 github.com/ipfs/go-log v1.0.5 github.com/jaypipes/ghw v0.24.0 diff --git a/go.sum b/go.sum index 244f166e0..7a15567c9 100644 --- a/go.sum +++ b/go.sum @@ -668,6 +668,8 @@ github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5T github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/gpustack/gguf-parser-go v0.25.0 h1:1AMBhMKtI24nTtn588Bq53FqNiOvEw1x9Nb4HbRrThs= github.com/gpustack/gguf-parser-go v0.25.0/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0= +github.com/gpustack/gguf-parser-go v0.26.3 h1:F63PlUPIW56HGU5k4Crv0JAG9ojtz/TuwzgMYlb82Ec= +github.com/gpustack/gguf-parser-go v0.26.3/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= diff --git a/pkg/vram/gguf_reader_test.go b/pkg/vram/gguf_reader_test.go index f1cd4a21e..a2983b99f 100644 --- a/pkg/vram/gguf_reader_test.go +++ b/pkg/vram/gguf_reader_test.go @@ -41,13 +41,23 @@ var _ = Describe("DefaultGGUFReader", func() { "large tokenizer metadata should be skipped with a bounds error") }) - It("converts a parser panic from malformed string metadata to an error", func() { + It("rejects an overflowing string before the parser allocates it", func() { server := serveGGUF(malformedGGUFString(uint64(math.MaxInt64))) _, err := vram.DefaultGGUFReader().ReadMetadata(context.Background(), server.URL+"/model.gguf") Expect(err).To(HaveOccurred()) - Expect(err.Error()).To(ContainSubstring("parser panic")) + Expect(err.Error()).NotTo(ContainSubstring("parser panic")) + Expect(err.Error()).To(ContainSubstring("remaining bytes")) + }) + + It("rejects a string longer than the remote file before allocating it", func() { + server := serveGGUF(malformedGGUFString(1 << 20)) + + _, err := vram.DefaultGGUFReader().ReadMetadata(context.Background(), server.URL+"/model.gguf") + + Expect(err).To(HaveOccurred()) + Expect(err.Error()).To(ContainSubstring("remaining bytes")) }) })