Enforce a limit of 400 million pixels, 2GB file size

This commit is contained in:
Dominic Szablewski 2021-12-16 20:12:27 +01:00
parent 11dbe1e6aa
commit 2f255c7aff
2 changed files with 33 additions and 7 deletions

View File

@ -7,8 +7,6 @@ the documentation.
More info at https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compression More info at https://phoboslab.org/log/2021/11/qoi-fast-lossless-image-compression
⚠️ Please note that this library is not yet ready to deal with untrusted input.
⚠️ 2021.11.30 the file format is not yet finalized. We're still working to fix ⚠️ 2021.11.30 the file format is not yet finalized. We're still working to fix
some smaller issues. The final specification will be announced on 2021.12.20. some smaller issues. The final specification will be announced on 2021.12.20.
Thanks for your patience! The WIP file format specification can be found in Thanks for your patience! The WIP file format specification can be found in
@ -30,6 +28,22 @@ converts between png <> qoi
a simple wrapper to benchmark stbi, libpng and qoi a simple wrapper to benchmark stbi, libpng and qoi
## Limitations
The QOI file format allows for huge images with up to 18 exa-pixels. A streaming
en-/decoder can handle these with minimal RAM requirements, assuming there is
enough storage space.
This particular implementation of QOI however is limited to images with a
maximum size of 400 million pixels. It will safely refuse to en-/decode anything
larger than that. This is not a streaming en-/decoder. It loads the whole image
file into RAM before doing any work and is not extensively optimized for
performance (but it's still very fast).
If this is a limitation for your use case, please look into any of the other
implementations listed below.
## Tools ## Tools
- https://github.com/floooh/qoiview - https://github.com/floooh/qoiview

22
qoi.h
View File

@ -168,8 +168,8 @@ Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as
The green channel is used to indicate the general direction of change and is The green channel is used to indicate the general direction of change and is
encoded in 6 bits. The red and green channels (dr and db) base their diffs off encoded in 6 bits. The red and green channels (dr and db) base their diffs off
of the green channel difference and are encoded in 4 bits. I.e.: of the green channel difference and are encoded in 4 bits. I.e.:
dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g) dr_dg = (last_px.r - cur_px.r) - (last_px.g - cur_px.g)
db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g) db_dg = (last_px.b - cur_px.b) - (last_px.g - cur_px.g)
The difference to the current channel values are using a wraparound operation, The difference to the current channel values are using a wraparound operation,
so "10 - 13" will result in 253, while "250 + 7" will result in 1. so "10 - 13" will result in 253, while "250 + 7" will result in 1.
@ -344,6 +344,12 @@ Implementation */
#define QOI_HEADER_SIZE 14 #define QOI_HEADER_SIZE 14
#define QOI_PADDING 8 #define QOI_PADDING 8
/* 2GB is the max file size that this implementation can safely handle. We guard
against anything larger than that, assuming the worst case with 5 bytes per
pixel, rounded down to a nice clean value. 400 million pixels ought to be
enough for anybody. */
#define QOI_PIXELS_MAX ((unsigned int)400000000)
typedef union { typedef union {
struct { unsigned char r, g, b, a; } rgba; struct { unsigned char r, g, b, a; } rgba;
unsigned int v; unsigned int v;
@ -376,7 +382,8 @@ void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
data == NULL || out_len == NULL || desc == NULL || data == NULL || out_len == NULL || desc == NULL ||
desc->width == 0 || desc->height == 0 || desc->width == 0 || desc->height == 0 ||
desc->channels < 3 || desc->channels > 4 || desc->channels < 3 || desc->channels > 4 ||
desc->colorspace > 2 desc->colorspace > 2 ||
desc->height >= QOI_PIXELS_MAX / desc->width
) { ) {
return NULL; return NULL;
} }
@ -502,7 +509,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
unsigned char *pixels; unsigned char *pixels;
qoi_rgba_t index[64]; qoi_rgba_t index[64];
qoi_rgba_t px; qoi_rgba_t px;
int px_len, chunks_len, px_pos; int px_len, chunks_len, px_pos;
int p = 0, run = 0; int p = 0, run = 0;
if ( if (
@ -525,7 +532,8 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
desc->width == 0 || desc->height == 0 || desc->width == 0 || desc->height == 0 ||
desc->channels < 3 || desc->channels > 4 || desc->channels < 3 || desc->channels > 4 ||
desc->colorspace > 2 || desc->colorspace > 2 ||
header_magic != QOI_MAGIC header_magic != QOI_MAGIC ||
desc->height >= QOI_PIXELS_MAX / desc->width
) { ) {
return NULL; return NULL;
} }
@ -636,6 +644,10 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) {
fseek(f, 0, SEEK_END); fseek(f, 0, SEEK_END);
size = ftell(f); size = ftell(f);
if (size <= 0) {
fclose(f);
return NULL;
}
fseek(f, 0, SEEK_SET); fseek(f, 0, SEEK_SET);
data = QOI_MALLOC(size); data = QOI_MALLOC(size);