Commit 2cea4a358d0 for php
commit 2cea4a358d0193e7afad630258f39e73d0977d27
Author: Daniel Scherzer <daniel.e.scherzer+phpf@gmail.com>
Date: Sun Sep 27 21:13:29 2026 -0700
ext/gd: fix undefined behavior with GIFs with problematic LZW compression data
Apply the changes from libgd/libgd@9fa3abd2e61da18ed2b889704e4e252f0f5a95fe in
order to fix undefined behavior from out-of-bounds reads when creating a GIF
with problematic LZW compression data.
diff --git a/ext/gd/libgd/gd_gif_in.c b/ext/gd/libgd/gd_gif_in.c
index 14c27f3293c..f2d9981688a 100644
--- a/ext/gd/libgd/gd_gif_in.c
+++ b/ext/gd/libgd/gd_gif_in.c
@@ -517,12 +517,20 @@ LWZReadByte_(gdIOCtx *fd, LZW_STATIC_DATA *sd, char flag, int input_code_size, i
/* Bad compressed data stream */
return -1;
}
+ if(code >= (1 << MAX_LWZ_BITS)) {
+ /* Corrupted code */
+ return -1;
+ }
*sd->sp++ = sd->table[1][code];
if (code == sd->table[0][code]) {
/* Oh well */
}
code = sd->table[0][code];
}
+ if(code >= (1 << MAX_LWZ_BITS)) {
+ /* Corrupted code */
+ return -1;
+ }
*sd->sp++ = sd->firstcode = sd->table[1][code];
diff --git a/ext/gd/tests/gif-oob.phpt b/ext/gd/tests/gif-oob.phpt
new file mode 100644
index 00000000000..73fd0427c8e
--- /dev/null
+++ b/ext/gd/tests/gif-oob.phpt
@@ -0,0 +1,63 @@
+--TEST--
+GIF OOB array access when using code size of 12
+--EXTENSIONS--
+gd
+--FILE--
+<?php
+
+$fileHeaderParts = [
+ "signature" => "GIF",
+ "version" => "89a",
+];
+$fileHeader = implode("", $fileHeaderParts);
+
+$logicalScreenDescriptorParts = [
+ // little-endian format
+ "width" => "\x04\x00",
+ "height" => "\x04\x00",
+ // packed data: global color table flag (most significant bit),
+ // color resolution (3 bits, only meaningful if global color table is enabled
+ // and we don't enable it here)
+ // sort flag (one bit, again only meaningful if global color table is enabled)
+ // size of global color table (3 bits)
+ "packed_info" => "\x00",
+ "background_color_index" => "\x00",
+ "pixel_aspect_ratio" => "\x00",
+];
+$logicalScreenDescriptor = implode("", $logicalScreenDescriptorParts);
+
+$startImage = ",";
+
+$imgDescParts = [
+ // little-ending format
+ "left" => "\x00\x00",
+ "top" => "\x00\x00",
+ "width" => "\x04\x00",
+ "height" => "\x04\x00",
+ // more packed data: local color table flag, interlace flag, sort flag,
+ // 2 bits reserved for future use, then 3 bits for size of local color
+ // table, which we don't have
+ "packed_info" => "\x00",
+];
+$imgDesc = implode("", $imgDescParts);
+
+$imgDataParts = [
+ "lzw_min_code_size" => "\x0c", // 12
+ "sub_block_num_bytes" => "\x05",
+ // Data in the block: 3 12-bit codes, and then 4 trailing 0 bits
+ "sub_block_bytes" => "\xff\x5f\x00\x06\x40",
+ // end of data
+ "end" => "\x00"
+];
+$imgData = implode("", $imgDataParts);
+
+$trailer = ";";
+
+$source = $fileHeader . $logicalScreenDescriptor . $startImage . $imgDesc . $imgData . $trailer;
+$img = imagecreatefromstring($source);
+var_dump($img);
+
+?>
+--EXPECTF--
+object(GdImage)#%d (0) {
+}