aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorHisham Aburaqibah <haburaqibah@giga.ly>2025-01-16 16:22:09 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2025-09-08 17:13:03 +0200
commit694593c5f27fd952b1c80917fa27d35023c290b6 (patch)
tree41120ca5f5f14523f78b30a486e8a448fdf0cf6e /core
parent8644f3bebabf462a42af6ad82c4b9cab8f3d8b4c (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.odin1
-rw-r--r--core/image/jpeg/jpeg.odin40
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)