summaryrefslogtreecommitdiffstats
path: root/php-cve-2025-14177.patch
blob: 036dcb03447ab5c1cd47f308a9449bb870420608 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
From 6cf0fb3c48fda6a236359304e4db663ae77d858c Mon Sep 17 00:00:00 2001
From: Niels Dossche <7771979+ndossche@users.noreply.github.com>
Date: Tue, 25 Nov 2025 23:11:38 +0100
Subject: [PATCH 1/5] Fix GH-20584: Information Leak of Memory

The string added had uninitialized memory due to
php_read_stream_all_chunks() not moving the buffer position, resulting
in the same data always being overwritten instead of new data being
added to the end of the buffer.

This is backport as there is a security impact as described in
GHSA-3237-qqm7-mfv7 .

(cherry picked from commit c5f28c7cf0a052f48e47877c7aa5c5bcc54f1cfc)
(cherry picked from commit ed665eb1903737d2b52b27368b155f6208604ed9)
---
 ext/standard/image.c                  | 20 +++++++++++---
 ext/standard/tests/image/gh20584.phpt | 39 +++++++++++++++++++++++++++
 2 files changed, 56 insertions(+), 3 deletions(-)
 create mode 100644 ext/standard/tests/image/gh20584.phpt

diff --git a/ext/standard/image.c b/ext/standard/image.c
index 4f2c5a505b4..800c5de8da3 100644
--- a/ext/standard/image.c
+++ b/ext/standard/image.c
@@ -434,8 +434,22 @@ static int php_skip_variable(php_stream * stream)
 }
 /* }}} */
 
-/* {{{ php_read_APP
- */
+static size_t php_read_stream_all_chunks(php_stream *stream, char *buffer, size_t length)
+{
+	size_t read_total = 0;
+	do {
+		ssize_t read_now = php_stream_read(stream, buffer, length - read_total);
+		read_total += read_now;
+		if (read_now < stream->chunk_size && read_total != length) {
+			return 0;
+		}
+		buffer += read_now;
+	} while (read_total < length);
+
+	return read_total;
+}
+
+/* {{{ php_read_APP */
 static int php_read_APP(php_stream * stream, unsigned int marker, zval *info)
 {
 	unsigned short length;
@@ -451,7 +465,7 @@ static int php_read_APP(php_stream * stream, unsigned int marker, zval *info)
 
 	buffer = emalloc((size_t)length);
 
-	if (php_stream_read(stream, buffer, (size_t) length) != length) {
+	if (php_read_stream_all_chunks(stream, buffer, length) != length) {
 		efree(buffer);
 		return 0;
 	}
diff --git a/ext/standard/tests/image/gh20584.phpt b/ext/standard/tests/image/gh20584.phpt
new file mode 100644
index 00000000000..d117f218202
--- /dev/null
+++ b/ext/standard/tests/image/gh20584.phpt
@@ -0,0 +1,39 @@
+--TEST--
+GH-20584 (Information Leak of Memory)
+--CREDITS--
+Nikita Sveshnikov (Positive Technologies)
+--FILE--
+<?php
+// Minimal PoC: corruption/uninitialized memory leak when reading APP1 via php://filter
+$file = __DIR__ . '/gh20584.jpg';
+
+// Make APP1 large enough so it is read in multiple chunks
+$chunk = 8192;
+$tail = 123;
+$payload = str_repeat('A', $chunk) . str_repeat('B', $chunk) . str_repeat('Z',
+$tail);
+$app1Len = 2 + strlen($payload);
+
+// Minimal JPEG: SOI + APP1 + SOF0(1x1) + EOI
+$sof = "\xFF\xC0" . pack('n', 11) . "\x08" . pack('n',1) . pack('n',1) .
+"\x01\x11\x00";
+$jpeg = "\xFF\xD8" . "\xFF\xE1" . pack('n', $app1Len) . $payload . $sof .
+"\xFF\xD9";
+file_put_contents($file, $jpeg);
+
+// Read through a filter to enforce multiple reads
+$src = 'php://filter/read=string.rot13|string.rot13/resource=' . $file;
+$info = null;
+@getimagesize($src, $info);
+$exp = $payload;
+$ret = $info['APP1'];
+
+var_dump($ret === $exp);
+
+?>
+--CLEAN--
+<?php
+@unlink(__DIR__ . '/gh20584.jpg');
+?>
+--EXPECT--
+bool(true)
-- 
2.52.0