From c71aba53fccd4fc9b0cd9c28c886642427fb4b99 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Thu, 2 Jul 2026 23:33:09 -0400 Subject: [PATCH 1/3] fix(gif): avoid int32 overflow in palette-split pixel-count math GifSplitPalette() computed `numPixels * (splitElt - firstElt)` as a 32-bit int before dividing. For any image over roughly 16.9M pixels (e.g. 4117x4117), that intermediate multiplication overflows INT_MAX and is undefined behavior; in practice it produced a bogus negative/garbage split count that could drive later indexing into `image` out of its bounds. Do the multiply in 64 bits and narrow back afterward -- the quotient is always <= numPixels, so the narrowing itself is safe. Assisted-by: Claude Code / Sonnet 5 Signed-off-by: Larry Gritz --- src/gif.imageio/gif.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gif.imageio/gif.h b/src/gif.imageio/gif.h index afbe3d8050..4ad7d87995 100644 --- a/src/gif.imageio/gif.h +++ b/src/gif.imageio/gif.h @@ -340,7 +340,8 @@ void GifSplitPalette(uint8_t* image, int numPixels, int firstElt, int lastElt, i if(bRange > gRange) splitCom = 2; if(rRange > bRange && rRange > gRange) splitCom = 0; - int subPixelsA = numPixels * (splitElt - firstElt) / (lastElt - firstElt); + int subPixelsA = (int)(int64_t(numPixels) * (splitElt - firstElt) + / (lastElt - firstElt)); int subPixelsB = numPixels-subPixelsA; GifPartitionByMedian(image, 0, numPixels, splitCom, subPixelsA); From 63ded659c228fe90ab7e60d7ecfadc754dd26bea Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Sat, 4 Jul 2026 20:30:09 -0500 Subject: [PATCH 2/3] More overflow protection Signed-off-by: Larry Gritz --- src/gif.imageio/gif.h | 4 ++-- src/gif.imageio/gifinput.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/gif.imageio/gif.h b/src/gif.imageio/gif.h index 4ad7d87995..624f05f8cf 100644 --- a/src/gif.imageio/gif.h +++ b/src/gif.imageio/gif.h @@ -389,7 +389,7 @@ void GifMakePalette( const uint8_t* lastFrame, const uint8_t* nextFrame, uint32_ // SplitPalette is destructive (it sorts the pixels by color) so // we must create a copy of the image for it to destroy - size_t imageSize = (size_t)(width * height * 4 * sizeof(uint8_t)); + size_t imageSize = size_t(width) * size_t(height) * 4 * sizeof(uint8_t); uint8_t* destroyableImage = (uint8_t*)GIF_TEMP_MALLOC(imageSize); memcpy(destroyableImage, nextFrame, imageSize); @@ -790,7 +790,7 @@ bool GifBegin( GifWriter* writer, const char* filename, uint32_t width, ui writer->firstFrame = true; // allocate - writer->oldImage = (uint8_t*)GIF_MALLOC(width*height*4); + writer->oldImage = (uint8_t*)GIF_MALLOC(uint64_t(width)*uint64_t(height)*4); fputs("GIF89a", writer->f); diff --git a/src/gif.imageio/gifinput.cpp b/src/gif.imageio/gifinput.cpp index c48a82cbaa..6b12c2fb25 100644 --- a/src/gif.imageio/gifinput.cpp +++ b/src/gif.imageio/gifinput.cpp @@ -206,7 +206,8 @@ GIFInput::read_native_scanline(int subimage, int miplevel, int y, int /*z*/, if (y < 0 || y > m_spec.height || !m_canvas.size()) return false; - memcpy(data, &m_canvas[y * m_spec.width * m_spec.nchannels], + memcpy(data, + &m_canvas[int64_t(y) * int64_t(m_spec.width) * m_spec.nchannels], m_spec.width * m_spec.nchannels); return true; @@ -366,7 +367,7 @@ GIFInput::read_subimage_data() return false; } int x = window_left + wx; - int idx = m_spec.nchannels * (y * m_spec.width + x); + int64_t idx = m_spec.nchannels * (int64_t(y) * m_spec.width + x); if (0 <= x && x < m_spec.width) { m_canvas[idx] = colormap[fscanline[wx]].Red; m_canvas[idx + 1] = colormap[fscanline[wx]].Green; From 3027b7d228823b99cb9d4933f81bb19bc934e25e Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Sun, 5 Jul 2026 16:58:54 -0700 Subject: [PATCH 3/3] fix formatting Signed-off-by: Larry Gritz --- src/gif.imageio/gifinput.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gif.imageio/gifinput.cpp b/src/gif.imageio/gifinput.cpp index 6b12c2fb25..8ea1aaf70a 100644 --- a/src/gif.imageio/gifinput.cpp +++ b/src/gif.imageio/gifinput.cpp @@ -366,8 +366,9 @@ GIFInput::read_subimage_data() fscanline[wx], wx, y, colormap_count); return false; } - int x = window_left + wx; - int64_t idx = m_spec.nchannels * (int64_t(y) * m_spec.width + x); + int x = window_left + wx; + int64_t idx = m_spec.nchannels + * (int64_t(y) * m_spec.width + x); if (0 <= x && x < m_spec.width) { m_canvas[idx] = colormap[fscanline[wx]].Red; m_canvas[idx + 1] = colormap[fscanline[wx]].Green;