diff options
| author | Hisham Aburaqibah <haburaqibah@giga.ly> | 2025-01-16 16:22:09 +0200 |
|---|---|---|
| committer | Jeroen van Rijn <Kelimion@users.noreply.github.com> | 2025-09-08 17:13:03 +0200 |
| commit | 694593c5f27fd952b1c80917fa27d35023c290b6 (patch) | |
| tree | 41120ca5f5f14523f78b30a486e8a448fdf0cf6e /core | |
| parent | 8644f3bebabf462a42af6ad82c4b9cab8f3d8b4c (diff) | |
image/jpeg: more bounds checking and skip malformed APP0
Also increase the maximum huffman symbols to 176
Diffstat (limited to 'core')
| -rw-r--r-- | core/image/common.odin | 1 | ||||
| -rw-r--r-- | core/image/jpeg/jpeg.odin | 40 |
2 files changed, 26 insertions, 15 deletions
diff --git a/core/image/common.odin b/core/image/common.odin index 5279166ef..a12760265 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -600,6 +600,7 @@ JPEG_Error :: enum { Encountered_RST_Marker_Outside_ECS, Extra_Data_After_SOS, // Image seemed to have decoded okay, but there's more data after SOS Invalid_Thumbnail_Size, + Huffman_Symbols_Exceeds_Max, } JFIF_Unit :: enum byte { diff --git a/core/image/jpeg/jpeg.odin b/core/image/jpeg/jpeg.odin index 945c51877..f675f90e7 100644 --- a/core/image/jpeg/jpeg.odin +++ b/core/image/jpeg/jpeg.odin @@ -13,7 +13,7 @@ Image :: image.Image Error :: image.Error Options :: image.Options -HUFFMAN_MAX_SYMBOLS :: 162 +HUFFMAN_MAX_SYMBOLS :: 176 HUFFMAN_MAX_BITS :: 16 // 768 bytes of 24-bit RGB values. THUMBNAIL_PALETTE_SIZE :: 768 @@ -75,21 +75,21 @@ refill_msb :: #force_inline proc(z: ^compress.Context_Memory_Input, width := i8( if len(z.input_data) != 0 { b = u64(z.input_data[0]) - if len(z.input_data) > 1 { + if len(z.input_data) > 1 && b == 0xFF { next := u64(z.input_data[1]) - if b == 0xFF { - if next == 0x00 { - // 0x00 is used as a stuffing to indicate that the 0xFF is part of the data and not - // the beginning of a marker - z.input_data = z.input_data[2:] - } else if next >= cast(u64)image.JPEG_Marker.RST0 && next <= cast(u64)image.JPEG_Marker.RST7 { - // Skip any RSTn markers if we encounter them + if next == 0x00 { + // 0x00 is used as a stuffing to indicate that the 0xFF is part of the data and not + // the beginning of a marker + z.input_data = z.input_data[2:] + } else if next >= cast(u64)image.JPEG_Marker.RST0 && next <= cast(u64)image.JPEG_Marker.RST7 { + // Skip any RSTn markers if we encounter them + if len(z.input_data) > 2 { b = u64(z.input_data[2]) z.input_data = z.input_data[3:] + } else { + b = 0 } - } else { - z.input_data = z.input_data[1:] } } else { z.input_data = z.input_data[1:] @@ -237,6 +237,12 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a append(&ident, b) } if slice.equal(ident[:], image.JFIF_Magic[:]) { + if length != 14 { + // Malformed APP0. Skip it + compress.read_slice(ctx, length - len(ident) - 1) or_return + continue + } + version := compress.read_data(ctx, u16be) or_return units := cast(image.JFIF_Unit)(compress.read_u8(ctx) or_return) x_density := compress.read_data(ctx, u16be) or_return @@ -437,13 +443,17 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a } lengths := compress.read_slice(ctx, HUFFMAN_MAX_BITS) or_return - num_symbols := 0 + num_symbols: u8 = 0 for length, i in lengths { - num_symbols += cast(int)length - huffman[type][index].offsets[i + 1] = cast(u8)num_symbols + num_symbols += length + huffman[type][index].offsets[i + 1] = num_symbols + } + + if num_symbols > HUFFMAN_MAX_SYMBOLS { + return img, .Huffman_Symbols_Exceeds_Max } - symbols := compress.read_slice(ctx, num_symbols) or_return + symbols := compress.read_slice(ctx, cast(int)num_symbols) or_return copy(huffman[type][index].symbols[:], symbols) length -= cast(u16be)(1 + HUFFMAN_MAX_BITS + num_symbols) |