Title: GIF: unbounded frame count grows memory with no cap
stbi__load_gif_main adds one layer per frame. layers has no upper bound.
Each layer costs wh4 plus sizeof(int) for delays.
Repro (header as-is, ASan build):
100 frames 32x32, 2320 bytes on disk, loads z=100, 409600 bytes resident.
10000 frames 8x8, 230020 bytes on disk, loads z=10000, 2560000 bytes resident.
20000 frames 1x1, 460020 bytes on disk, loads z=20000.
Growth is linear. No warning.
Code:
stb_image.h, stbi__load_gif_main, around line 6990.
++layers, then malloc/realloc(layersstride) and layerssizeof(int) with
raw int math. w and h go through mad3sizes_valid. layers does not.
Past ~524288 frames at 32x32 the int wraps. I did not trigger that part
here. I report DoS only.
Files to repro: generator in stb_gif_layers.cpp (makes minimal GIFs with
N frames). Build with clang++ -fsanitize=address -I. against stb_image.h.
Title: GIF: unbounded frame count grows memory with no cap
stbi__load_gif_main adds one layer per frame. layers has no upper bound.
Each layer costs wh4 plus sizeof(int) for delays.
Repro (header as-is, ASan build):
100 frames 32x32, 2320 bytes on disk, loads z=100, 409600 bytes resident.
10000 frames 8x8, 230020 bytes on disk, loads z=10000, 2560000 bytes resident.
20000 frames 1x1, 460020 bytes on disk, loads z=20000.
Growth is linear. No warning.
Code:
stb_image.h, stbi__load_gif_main, around line 6990.
++layers, then malloc/realloc(layersstride) and layerssizeof(int) with
raw int math. w and h go through mad3sizes_valid. layers does not.
Past ~524288 frames at 32x32 the int wraps. I did not trigger that part
here. I report DoS only.
Files to repro: generator in stb_gif_layers.cpp (makes minimal GIFs with
N frames). Build with clang++ -fsanitize=address -I. against stb_image.h.