Browse Source

Fix parsing of HEIF files with large boxes

DarthSim 1 year ago
parent
commit
bf9e54620f
2 changed files with 33 additions and 18 deletions
  1. 2 0
      CHANGELOG.md
  2. 31 18
      imagemeta/heif.go

+ 2 - 0
CHANGELOG.md

@@ -1,6 +1,8 @@
 # Changelog
 
 ## [Unreleased]
+### Fix
+- Fix parsing of HEIF files with large boxes.
 
 ## [3.19.0] - 2023-08-21
 ### Add

+ 31 - 18
imagemeta/heif.go

@@ -6,11 +6,12 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"math"
 
 	"github.com/imgproxy/imgproxy/v3/imagetype"
 )
 
-const heifBoxHeaderSize = int64(8)
+const heifBoxHeaderSize = uint64(8)
 
 var heicBrand = []byte("heic")
 var avifBrand = []byte("avif")
@@ -29,7 +30,7 @@ func (d *heifData) IsFilled() bool {
 	return d.Format != imagetype.Unknown && d.Width > 0 && d.Height > 0
 }
 
-func heifReadN(r io.Reader, n int64) (b []byte, err error) {
+func heifReadN(r io.Reader, n uint64) (b []byte, err error) {
 	if buf, ok := r.(*bytes.Buffer); ok {
 		b = buf.Next(int(n))
 		if len(b) == 0 {
@@ -43,7 +44,7 @@ func heifReadN(r io.Reader, n int64) (b []byte, err error) {
 	return
 }
 
-func heifDiscardN(r io.Reader, n int64) error {
+func heifDiscardN(r io.Reader, n uint64) error {
 	if buf, ok := r.(*bytes.Buffer); ok {
 		_ = buf.Next(int(n))
 		return nil
@@ -54,11 +55,11 @@ func heifDiscardN(r io.Reader, n int64) error {
 		return err
 	}
 
-	_, err := io.CopyN(io.Discard, r, n)
+	_, err := io.CopyN(io.Discard, r, int64(n))
 	return err
 }
 
-func heifReadBoxHeader(r io.Reader) (boxType string, boxDataSize int64, err error) {
+func heifReadBoxHeader(r io.Reader) (boxType string, boxDataSize uint64, err error) {
 	var b []byte
 
 	b, err = heifReadN(r, heifBoxHeaderSize)
@@ -66,9 +67,28 @@ func heifReadBoxHeader(r io.Reader) (boxType string, boxDataSize int64, err erro
 		return
 	}
 
-	boxDataSize = int64(binary.BigEndian.Uint32(b[0:4])) - heifBoxHeaderSize
+	headerSize := heifBoxHeaderSize
+
+	boxDataSize = uint64(binary.BigEndian.Uint32(b[0:4]))
 	boxType = string(b[4:8])
 
+	if boxDataSize == 1 {
+		b, err = heifReadN(r, 8)
+		if err != nil {
+			return
+		}
+
+		boxDataSize = (uint64(binary.BigEndian.Uint32(b[0:4])) << 32) |
+			uint64(binary.BigEndian.Uint32(b[4:8]))
+		headerSize += 8
+	}
+
+	if boxDataSize < heifBoxHeaderSize || boxDataSize > math.MaxInt64 {
+		return "", 0, errors.New("Invalid box data size")
+	}
+
+	boxDataSize -= headerSize
+
 	return
 }
 
@@ -86,7 +106,7 @@ func heifAssignFormat(d *heifData, brand []byte) bool {
 	return false
 }
 
-func heifReadFtyp(d *heifData, r io.Reader, boxDataSize int64) error {
+func heifReadFtyp(d *heifData, r io.Reader, boxDataSize uint64) error {
 	if boxDataSize < 8 {
 		return errors.New("Invalid ftyp data")
 	}
@@ -101,7 +121,7 @@ func heifReadFtyp(d *heifData, r io.Reader, boxDataSize int64) error {
 	}
 
 	if boxDataSize >= 12 {
-		for i := int64(8); i < boxDataSize; i += 4 {
+		for i := uint64(8); i < boxDataSize; i += 4 {
 			if heifAssignFormat(d, data[i:i+4]) {
 				return nil
 			}
@@ -111,7 +131,7 @@ func heifReadFtyp(d *heifData, r io.Reader, boxDataSize int64) error {
 	return errors.New("Image is not compatible with heic/avif")
 }
 
-func heifReadMeta(d *heifData, r io.Reader, boxDataSize int64) error {
+func heifReadMeta(d *heifData, r io.Reader, boxDataSize uint64) error {
 	if boxDataSize < 4 {
 		return errors.New("Invalid meta data")
 	}
@@ -130,7 +150,7 @@ func heifReadMeta(d *heifData, r io.Reader, boxDataSize int64) error {
 	return nil
 }
 
-func heifReadHldr(r io.Reader, boxDataSize int64) error {
+func heifReadHldr(r io.Reader, boxDataSize uint64) error {
 	if boxDataSize < 12 {
 		return errors.New("Invalid hdlr data")
 	}
@@ -147,7 +167,7 @@ func heifReadHldr(r io.Reader, boxDataSize int64) error {
 	return nil
 }
 
-func heifReadIspe(r io.Reader, boxDataSize int64) (w, h int64, err error) {
+func heifReadIspe(r io.Reader, boxDataSize uint64) (w, h int64, err error) {
 	if boxDataSize < 12 {
 		return 0, 0, errors.New("Invalid ispe data")
 	}
@@ -166,17 +186,10 @@ func heifReadIspe(r io.Reader, boxDataSize int64) (w, h int64, err error) {
 func heifReadBoxes(d *heifData, r io.Reader) error {
 	for {
 		boxType, boxDataSize, err := heifReadBoxHeader(r)
-
 		if err != nil {
 			return err
 		}
 
-		if boxDataSize < 0 {
-			return errors.New("Invalid box data")
-		}
-
-		// log.Printf("Box type: %s; Box data size: %d", boxType, boxDataSize)
-
 		switch boxType {
 		case "ftyp":
 			if err := heifReadFtyp(d, r, boxDataSize); err != nil {