aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2021-10-06 21:48:22 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2021-10-06 21:48:22 +0200
commit8fcd1794a676610f838bc00b754f80830d6c6e50 (patch)
tree390a43d279fbf3938522e65a8b98b13cecb2cf25 /core
parent9b5ae9567790d0f340d6ef56ff993e39f0b4873a (diff)
png: Add sane compile-time maximums for dimensions + chunk sizes.
Diffstat (limited to 'core')
-rw-r--r--core/compress/common.odin3
-rw-r--r--core/image/common.odin7
-rw-r--r--core/image/png/example.odin7
-rw-r--r--core/image/png/helpers.odin3
-rw-r--r--core/image/png/png.odin60
5 files changed, 63 insertions, 17 deletions
diff --git a/core/compress/common.odin b/core/compress/common.odin
index 819cfb481..41f292b6f 100644
--- a/core/compress/common.odin
+++ b/core/compress/common.odin
@@ -1,5 +1,3 @@
-package compress
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
@@ -7,6 +5,7 @@ package compress
List of contributors:
Jeroen van Rijn: Initial implementation, optimization.
*/
+package compress
import "core:io"
import "core:bytes"
diff --git a/core/image/common.odin b/core/image/common.odin
index 9bb99a5d4..2826a65ca 100644
--- a/core/image/common.odin
+++ b/core/image/common.odin
@@ -1,13 +1,12 @@
-package image
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
- Made available under Odin's BSD-2 license.
+ Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation, optimization.
Ginger Bill: Cosmetic changes.
*/
+package image
import "core:bytes"
import "core:mem"
@@ -128,6 +127,7 @@ Error :: union {
General_Image_Error :: enum {
None = 0,
Invalid_Image_Dimensions,
+ Image_Dimensions_Too_Large,
Image_Does_Not_Adhere_to_Spec,
}
@@ -138,6 +138,7 @@ PNG_Error :: enum {
IDAT_Missing,
IDAT_Must_Be_Contiguous,
IDAT_Corrupt,
+ IDAT_Size_Too_Large,
PLTE_Encountered_Unexpectedly,
PLTE_Invalid_Length,
TRNS_Encountered_Unexpectedly,
diff --git a/core/image/png/example.odin b/core/image/png/example.odin
index ad0423d67..5370b0bcf 100644
--- a/core/image/png/example.odin
+++ b/core/image/png/example.odin
@@ -1,9 +1,6 @@
-//+ignore
-package png
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
- Made available under Odin's BSD-2 license.
+ Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
@@ -11,6 +8,8 @@ package png
An example of how to use `load`.
*/
+//+ignore
+package png
import "core:image"
// import "core:image/png"
diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin
index 1b3749f76..59d8fb70b 100644
--- a/core/image/png/helpers.odin
+++ b/core/image/png/helpers.odin
@@ -1,5 +1,3 @@
-package png
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-2 license.
@@ -10,6 +8,7 @@ package png
These are a few useful utility functions to work with PNG images.
*/
+package png
import "core:image"
import "core:compress/zlib"
diff --git a/core/image/png/png.odin b/core/image/png/png.odin
index 21b27fc82..883e51d83 100644
--- a/core/image/png/png.odin
+++ b/core/image/png/png.odin
@@ -1,13 +1,12 @@
-package png
-
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
- Made available under Odin's BSD-2 license.
+ Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
Ginger Bill: Cosmetic changes.
*/
+package png
import "core:compress"
import "core:compress/zlib"
@@ -21,6 +20,28 @@ import "core:io"
import "core:mem"
import "core:intrinsics"
+/*
+ 67_108_864 pixels max by default.
+ Maximum allowed dimensions are capped at 65535 * 65535.
+*/
+MAX_DIMENSIONS :: min(#config(PNG_MAX_DIMENSIONS, 8192 * 8192), 65535 * 65535)
+
+/*
+ Limit chunk sizes.
+ By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
+*/
+_MAX_IDAT_DEFAULT :: ( 8192 /* Width */ * 8192 /* Height */ * 2 /* 16-bit */) + 8192 /* Filter bytes */
+_MAX_IDAT :: (65535 /* Width */ * 65535 /* Height */ * 2 /* 16-bit */) + 65535 /* Filter bytes */
+
+MAX_IDAT_SIZE :: min(#config(PNG_MAX_IDAT_SIZE, _MAX_IDAT_DEFAULT), _MAX_IDAT)
+
+/*
+ For chunks other than IDAT with a variable size like `zTXT` and `eXIf`,
+ limit their size to 16 MiB each by default. Max of 256 MiB each.
+*/
+MAX_CHUNK_SIZE :: min(#config(PNG_MAX_CHUNK_SIZE, 16_777_216), 268_435_456)
+
+
Error :: image.Error
Image :: image.Image
Options :: image.Options
@@ -248,6 +269,20 @@ read_chunk :: proc(ctx: ^$C) -> (chunk: Chunk, err: Error) {
}
chunk.header = ch
+ /*
+ Sanity check chunk size
+ */
+ #partial switch ch.type {
+ case .IDAT:
+ if ch.length > MAX_IDAT_SIZE {
+ return {}, image.PNG_Error.IDAT_Size_Too_Large
+ }
+ case:
+ if ch.length > MAX_CHUNK_SIZE {
+ return {}, image.PNG_Error.Invalid_Chunk_Length
+ }
+ }
+
chunk.data, e = compress.read_slice(ctx, int(ch.length))
if e != .None {
return {}, compress.General_Error.Stream_Too_Short
@@ -308,7 +343,7 @@ read_header :: proc(ctx: ^$C) -> (IHDR, Error) {
header := (^IHDR)(raw_data(c.data))^
// Validate IHDR
using header
- if width == 0 || height == 0 {
+ if width == 0 || height == 0 || u128(width) * u128(height) > MAX_DIMENSIONS {
return {}, .Invalid_Image_Dimensions
}
@@ -438,9 +473,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
idat: []u8
idat_b: bytes.Buffer
- idat_length := u32be(0)
defer bytes.buffer_destroy(&idat_b)
+ idat_length := u64(0)
+
c: Chunk
ch: Chunk_Header
e: io.Error
@@ -521,6 +557,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
interlace_method = interlace_method,
}
info.header = h
+
case .PLTE:
seen_plte = true
// PLTE must appear before IDAT and can't appear for color types 0, 4.
@@ -543,6 +580,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
if .return_metadata in options {
append_chunk(&info.chunks, c) or_return
}
+
case .IDAT:
// If we only want image metadata and don't want the pixel data, we can early out.
if .return_metadata not_in options && .do_not_decompress_image in options {
@@ -563,7 +601,11 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
c = read_chunk(ctx) or_return
bytes.buffer_write(&idat_b, c.data)
- idat_length += c.header.length
+ idat_length += u64(c.header.length)
+
+ if idat_length > MAX_IDAT_SIZE {
+ return {}, image.PNG_Error.IDAT_Size_Too_Large
+ }
ch, e = compress.peek_data(ctx, Chunk_Header)
if e != .None {
@@ -571,14 +613,17 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
next = ch.type
}
+
idat = bytes.buffer_to_bytes(&idat_b)
if int(idat_length) != len(idat) {
return {}, .IDAT_Corrupt
}
seen_idat = true
+
case .IEND:
c = read_chunk(ctx) or_return
seen_iend = true
+
case .bKGD:
// TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be
@@ -614,6 +659,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
col := mem.slice_data_cast([]u16be, c.data[:])
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}
}
+
case .tRNS:
c = read_chunk(ctx) or_return
@@ -645,6 +691,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
}
trns = c
+
case .iDOT, .CbGI:
/*
iPhone PNG bastardization that doesn't adhere to spec with broken IDAT chunk.
@@ -652,6 +699,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
across one of these files, use a utility to defry it.
*/
return img, .Image_Does_Not_Adhere_to_Spec
+
case:
// Unhandled type
c = read_chunk(ctx) or_return