commit 4a731eacbb1861e866e84024aba201f1cbf0f22f Author: Anthony Hurtado Date: Mon Jun 1 15:41:11 2026 -0500 Fix integer overflow and type safety issues across giflib Multiple integer overflow vulnerabilities exist in giflib's pixel count and dimension arithmetic. On 32-bit platforms where long and int are 32 bits, these overflow to negative values or wrap around, causing heap buffer overflows, incorrect loop bounds, and undefined behavior. dgif_lib.c: - PixelCount: cast to (unsigned long) instead of signed (long) to prevent signed overflow UB on 32-bit platforms. - LZW CodeSize: reject BitsPerPixel < 2 (CodeSize 0 and 1 create degenerate LZW states where ClearCode/EOFCode are unreachable). egif_lib.c: - PixelCount: same (unsigned long) fix as dgif_lib.c. - EGifSpew: cast j to (size_t) in pointer arithmetic j * SavedWidth to prevent int*int overflow in raster offset computation. gifalloc.c: - GifApplyTranslation: use size_t for RasterSize and loop counter instead of int to prevent Height*Width overflow. - GifMakeSavedImage: cast Height to (size_t) in reallocarray nmemb and memcpy size to prevent int*int overflow before reaching reallocarray's internal overflow check. quantize.c: - Compute PixelCount as (size_t)Width * Height with division-based overflow guard. Use size_t loop counter for pixel iteration. - Replace signed (long) cast of pixel count with (unsigned long). All 51 regression tests pass. Compiles with zero warnings under -Wall -O2. Signed-off-by: Anthony Hurtado diff --git dgif_lib.c dgif_lib.c index cec94c1..a022bb6 100644 --- dgif_lib.c +++ dgif_lib.c @@ -416,7 +416,8 @@ int DGifGetImageHeader(GifFileType *GifFile) { } Private->PixelCount = - (long)GifFile->Image.Width * (long)GifFile->Image.Height; + (unsigned long)GifFile->Image.Width * + (unsigned long)GifFile->Image.Height; /* Reset decompress algorithm parameters. */ return DGifSetupDecompress(GifFile); @@ -824,7 +825,7 @@ static int DGifSetupDecompress(GifFileType *GifFile) { BitsPerPixel = CodeSize; /* this can only happen on a severely malformed GIF */ - if (BitsPerPixel > 8) { + if (BitsPerPixel < 2 || BitsPerPixel > 8) { GifFile->Error = D_GIF_ERR_READ_FAILED; /* somewhat bogus error code */ return GIF_ERROR; /* Failed to read Code size. */ diff --git egif_lib.c egif_lib.c index d74e8df..6f7c9b6 100644 --- egif_lib.c +++ egif_lib.c @@ -448,7 +448,8 @@ int EGifPutImageDesc(GifFileType *GifFile, const int Left, const int Top, /* Mark this file as has screen descriptor: */ Private->FileState |= FILE_STATE_IMAGE; - Private->PixelCount = (long)Width * (long)Height; + Private->PixelCount = + (unsigned long)Width * (unsigned long)Height; /* Reset compress algorithm parameters. */ (void)EGifSetupCompress(GifFile); @@ -1154,7 +1155,7 @@ int EGifSpew(GifFileType *GifFileOut, int *ErrorCode) { j += InterlacedJumps[k]) { if (EGifPutLine( GifFileOut, - sp->RasterBits + j * SavedWidth, + sp->RasterBits + (size_t)j * SavedWidth, SavedWidth) == GIF_ERROR) { status = GIF_ERROR; err = GifFileOut->Error; @@ -1165,7 +1166,7 @@ int EGifSpew(GifFileType *GifFileOut, int *ErrorCode) { } else { for (j = 0; j < SavedHeight; j++) { if (EGifPutLine(GifFileOut, - sp->RasterBits + j * SavedWidth, + sp->RasterBits + (size_t)j * SavedWidth, SavedWidth) == GIF_ERROR) { status = GIF_ERROR; err = GifFileOut->Error; diff --git gifalloc.c gifalloc.c index 3d897ab..886907d 100644 --- gifalloc.c +++ gifalloc.c @@ -210,9 +210,9 @@ ColorMapObject *GifUnionColorMap(const ColorMapObject *ColorIn1, Apply a given color translation to the raster bits of an image *******************************************************************************/ void GifApplyTranslation(SavedImage *Image, const GifPixelType Translation[]) { - register int i; - register int RasterSize = - Image->ImageDesc.Height * Image->ImageDesc.Width; + size_t i; + size_t RasterSize = + (size_t)Image->ImageDesc.Height * Image->ImageDesc.Width; for (i = 0; i < RasterSize; i++) { Image->RasterBits[i] = Translation[Image->RasterBits[i]]; @@ -371,17 +371,17 @@ SavedImage *GifMakeSavedImage(GifFileType *GifFile, /* next, the raster */ sp->RasterBits = (unsigned char *)reallocarray( NULL, - (CopyFrom->ImageDesc.Height * - CopyFrom->ImageDesc.Width), + (size_t)CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width, sizeof(GifPixelType)); if (sp->RasterBits == NULL) { FreeLastSavedImage(GifFile); return (SavedImage *)(NULL); } memcpy(sp->RasterBits, CopyFrom->RasterBits, - sizeof(GifPixelType) * - CopyFrom->ImageDesc.Height * - CopyFrom->ImageDesc.Width); + (size_t)CopyFrom->ImageDesc.Height * + CopyFrom->ImageDesc.Width * + sizeof(GifPixelType)); /* finally, the extension blocks */ if (CopyFrom->ExtensionBlocks != NULL) { diff --git quantize.c quantize.c index 160a29b..4ea44ce 100644 --- quantize.c +++ quantize.c @@ -71,6 +71,7 @@ int GifQuantizeBuffer(unsigned int Width, unsigned int Height, long Red, Green, Blue; NewColorMapType NewColorSubdiv[256]; QuantizedColorType *ColorArrayEntries, *QuantizedColor; + size_t k, PixelCount; ColorArrayEntries = (QuantizedColorType *)malloc( sizeof(QuantizedColorType) * COLOR_ARRAY_SIZE); @@ -78,6 +79,12 @@ int GifQuantizeBuffer(unsigned int Width, unsigned int Height, return GIF_ERROR; } + PixelCount = (size_t)Width * Height; + if (Width != 0 && PixelCount / Width != Height) { + free((char *)ColorArrayEntries); + return GIF_ERROR; + } + for (i = 0; i < COLOR_ARRAY_SIZE; i++) { ColorArrayEntries[i].RGB[0] = i >> (2 * BITS_PER_PRIM_COLOR); ColorArrayEntries[i].RGB[1] = @@ -87,12 +94,12 @@ int GifQuantizeBuffer(unsigned int Width, unsigned int Height, } /* Sample the colors and their distribution: */ - for (i = 0; i < (int)(Width * Height); i++) { - Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) + for (k = 0; k < PixelCount; k++) { + Index = ((RedInput[k] >> (8 - BITS_PER_PRIM_COLOR)) << (2 * BITS_PER_PRIM_COLOR)) + - ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) + ((GreenInput[k] >> (8 - BITS_PER_PRIM_COLOR)) << BITS_PER_PRIM_COLOR) + - (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); + (BlueInput[k] >> (8 - BITS_PER_PRIM_COLOR)); ColorArrayEntries[Index].Count++; } @@ -127,7 +134,7 @@ int GifQuantizeBuffer(unsigned int Width, unsigned int Height, NewColorSubdiv[0].NumEntries = NumOfEntries; /* Different sampled colors */ - NewColorSubdiv[0].Count = ((long)Width) * Height; /* Pixels */ + NewColorSubdiv[0].Count = (unsigned long)PixelCount; /* Pixels */ NewColorMapSize = 1; if (SubdivColorMap(NewColorSubdiv, *ColorMapSize, &NewColorMapSize) != GIF_OK) { @@ -167,28 +174,28 @@ int GifQuantizeBuffer(unsigned int Width, unsigned int Height, /* Finally scan the input buffer again and put the mapped index in the * output buffer. */ MaxRGBError[0] = MaxRGBError[1] = MaxRGBError[2] = 0; - for (i = 0; i < (int)(Width * Height); i++) { - Index = ((RedInput[i] >> (8 - BITS_PER_PRIM_COLOR)) + for (k = 0; k < PixelCount; k++) { + Index = ((RedInput[k] >> (8 - BITS_PER_PRIM_COLOR)) << (2 * BITS_PER_PRIM_COLOR)) + - ((GreenInput[i] >> (8 - BITS_PER_PRIM_COLOR)) + ((GreenInput[k] >> (8 - BITS_PER_PRIM_COLOR)) << BITS_PER_PRIM_COLOR) + - (BlueInput[i] >> (8 - BITS_PER_PRIM_COLOR)); + (BlueInput[k] >> (8 - BITS_PER_PRIM_COLOR)); Index = ColorArrayEntries[Index].NewColorIndex; - OutputBuffer[i] = Index; + OutputBuffer[k] = Index; if (MaxRGBError[0] < - ABS(OutputColorMap[Index].Red - RedInput[i])) { + ABS(OutputColorMap[Index].Red - RedInput[k])) { MaxRGBError[0] = - ABS(OutputColorMap[Index].Red - RedInput[i]); + ABS(OutputColorMap[Index].Red - RedInput[k]); } if (MaxRGBError[1] < - ABS(OutputColorMap[Index].Green - GreenInput[i])) { + ABS(OutputColorMap[Index].Green - GreenInput[k])) { MaxRGBError[1] = - ABS(OutputColorMap[Index].Green - GreenInput[i]); + ABS(OutputColorMap[Index].Green - GreenInput[k]); } if (MaxRGBError[2] < - ABS(OutputColorMap[Index].Blue - BlueInput[i])) { + ABS(OutputColorMap[Index].Blue - BlueInput[k])) { MaxRGBError[2] = - ABS(OutputColorMap[Index].Blue - BlueInput[i]); + ABS(OutputColorMap[Index].Blue - BlueInput[k]); } }