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) {
+}