font/sfnt: apply bounds checks before allocating read buffer When using ReadAt to read more than 1MiB of data from a font file, verify that the file contains the data before allocating the read buffer. Avoids excessive memory allocation when parsing corrupt or malicious font files. Thanks to Andy Gill, ZephrSec Ltd for reporting this issue. Fixes golang/go#78382 Fixes CVE-2026-33812 Change-Id: Icd5e7388661a76a6af800f0ba0b728c46a6a6964 Reviewed-on: https://go-review.googlesource.com/c/image/+/761180 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Neal Patel <nealpatel@google.com>
diff --git a/font/sfnt/sfnt.go b/font/sfnt/sfnt.go index 8ed19e2..d1ef8a6 100644 --- a/font/sfnt/sfnt.go +++ b/font/sfnt/sfnt.go
@@ -214,8 +214,9 @@ // copying from the source to a caller-supplied buffer, and instead provide // direct access to the underlying []byte data. type source struct { - b []byte - r io.ReaderAt + b []byte + r io.ReaderAt + minSize int // r is known to contain at least minSize bytes // TODO: add a caching layer, if we're using the io.ReaderAt? Note that // this might make a source no longer safe to use concurrently. @@ -255,6 +256,17 @@ return s.b[offset : offset+length], nil } + if end := offset + length; end > s.minSize && length > 1<<20 { + // We're reading more than 1MiB, and we don't know whether + // the file contains this data. Check that the data exists + // before we try to allocate. + var oneByte [1]byte + if n, err := s.r.ReadAt(oneByte[:], int64(end)-1); err != nil || n != 1 { + return nil, errInvalidBounds + } + s.minSize = end + } + // Read from the io.ReaderAt. if length <= cap(buf) { buf = buf[:length]