Skip to content

Commit b582676

Browse files
committed
Update PHP patches and installer key handling [skip ci]
1 parent 8e236ac commit b582676

19 files changed

Lines changed: 1354 additions & 1 deletion
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
From: Niels Dossche <7771979+ndossche@users.noreply.github.com>
2+
Date: Tue, 25 Nov 2025 23:11:38 +0100
3+
Subject: Fix GH-20584: Information Leak of Memory
4+
5+
The string added had uninitialized memory due to
6+
php_read_stream_all_chunks() not moving the buffer position, resulting
7+
in the same data always being overwritten instead of new data being
8+
added to the end of the buffer.
9+
10+
This is backport as there is a security impact as described in
11+
GHSA-3237-qqm7-mfv7 .
12+
13+
(cherry picked from commit c5f28c7cf0a052f48e47877c7aa5c5bcc54f1cfc)
14+
(cherry picked from commit ed665eb1903737d2b52b27368b155f6208604ed9)
15+
---
16+
ext/standard/image.c | 20 +++++++++++++++---
17+
ext/standard/tests/image/gh20584.phpt | 39 +++++++++++++++++++++++++++++++++++
18+
2 files changed, 56 insertions(+), 3 deletions(-)
19+
create mode 100644 ext/standard/tests/image/gh20584.phpt
20+
21+
diff --git a/ext/standard/image.c b/ext/standard/image.c
22+
index fc4d6bd..06f0a00 100644
23+
--- a/ext/standard/image.c
24+
+++ b/ext/standard/image.c
25+
@@ -434,8 +434,22 @@ static int php_skip_variable(php_stream * stream)
26+
}
27+
/* }}} */
28+
29+
-/* {{{ php_read_APP
30+
- */
31+
+static size_t php_read_stream_all_chunks(php_stream *stream, char *buffer, size_t length)
32+
+{
33+
+ size_t read_total = 0;
34+
+ do {
35+
+ ssize_t read_now = php_stream_read(stream, buffer, length - read_total);
36+
+ read_total += read_now;
37+
+ if (read_now < stream->chunk_size && read_total != length) {
38+
+ return 0;
39+
+ }
40+
+ buffer += read_now;
41+
+ } while (read_total < length);
42+
+
43+
+ return read_total;
44+
+}
45+
+
46+
+/* {{{ php_read_APP */
47+
static int php_read_APP(php_stream * stream, unsigned int marker, zval *info)
48+
{
49+
unsigned short length;
50+
@@ -451,7 +465,7 @@ static int php_read_APP(php_stream * stream, unsigned int marker, zval *info)
51+
52+
buffer = emalloc(length);
53+
54+
- if (php_stream_read(stream, buffer, (zend_long) length) != length) {
55+
+ if (php_read_stream_all_chunks(stream, buffer, length) != length) {
56+
efree(buffer);
57+
return 0;
58+
}
59+
diff --git a/ext/standard/tests/image/gh20584.phpt b/ext/standard/tests/image/gh20584.phpt
60+
new file mode 100644
61+
index 0000000..d117f21
62+
--- /dev/null
63+
+++ b/ext/standard/tests/image/gh20584.phpt
64+
@@ -0,0 +1,39 @@
65+
+--TEST--
66+
+GH-20584 (Information Leak of Memory)
67+
+--CREDITS--
68+
+Nikita Sveshnikov (Positive Technologies)
69+
+--FILE--
70+
+<?php
71+
+// Minimal PoC: corruption/uninitialized memory leak when reading APP1 via php://filter
72+
+$file = __DIR__ . '/gh20584.jpg';
73+
+
74+
+// Make APP1 large enough so it is read in multiple chunks
75+
+$chunk = 8192;
76+
+$tail = 123;
77+
+$payload = str_repeat('A', $chunk) . str_repeat('B', $chunk) . str_repeat('Z',
78+
+$tail);
79+
+$app1Len = 2 + strlen($payload);
80+
+
81+
+// Minimal JPEG: SOI + APP1 + SOF0(1x1) + EOI
82+
+$sof = "\xFF\xC0" . pack('n', 11) . "\x08" . pack('n',1) . pack('n',1) .
83+
+"\x01\x11\x00";
84+
+$jpeg = "\xFF\xD8" . "\xFF\xE1" . pack('n', $app1Len) . $payload . $sof .
85+
+"\xFF\xD9";
86+
+file_put_contents($file, $jpeg);
87+
+
88+
+// Read through a filter to enforce multiple reads
89+
+$src = 'php://filter/read=string.rot13|string.rot13/resource=' . $file;
90+
+$info = null;
91+
+@getimagesize($src, $info);
92+
+$exp = $payload;
93+
+$ret = $info['APP1'];
94+
+
95+
+var_dump($ret === $exp);
96+
+
97+
+?>
98+
+--CLEAN--
99+
+<?php
100+
+@unlink(__DIR__ . '/gh20584.jpg');
101+
+?>
102+
+--EXPECT--
103+
+bool(true)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
From: Jakub Zelenka <bukka@php.net>
2+
Date: Sat, 11 Oct 2025 19:37:26 +0200
3+
Subject: Only test from (code is not affected)
4+
5+
Fix GHSA-8xr5-qppj-gvwj: PDO quoting result null deref
6+
7+
(cherry picked from commit 727a4ddc39ca9b78131e06b5537fe8b6c3dd6fe7)
8+
(cherry picked from commit 652cb22d27172e8068bc286774fb2b7078427093)
9+
---
10+
ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt | 26 ++++++++++++++++++++++++++
11+
1 file changed, 26 insertions(+)
12+
create mode 100644 ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt
13+
14+
diff --git a/ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt b/ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt
15+
new file mode 100644
16+
index 0000000..019a02c
17+
--- /dev/null
18+
+++ b/ext/pdo_pgsql/tests/ghsa-8xr5-qppj-gvwj.phpt
19+
@@ -0,0 +1,26 @@
20+
+--TEST--
21+
+#GHSA-8xr5-qppj-gvwj: NULL Pointer Derefernce for failed user input quoting
22+
+--SKIPIF--
23+
+<?php
24+
+if (!extension_loaded('pdo') || !extension_loaded('pdo_pgsql')) die('skip not loaded');
25+
+require_once dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
26+
+require_once dirname(__FILE__) . '/config.inc';
27+
+PDOTest::skip();
28+
+?>
29+
+--FILE--
30+
+<?php
31+
+require_once dirname(__FILE__) . '/../../../ext/pdo/tests/pdo_test.inc';
32+
+require_once dirname(__FILE__) . '/config.inc';
33+
+$db = PDOTest::test_factory(dirname(__FILE__) . '/common.phpt');
34+
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
35+
+$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
36+
+
37+
+$sql = "SELECT * FROM users where username = :username";
38+
+$stmt = $db->prepare($sql);
39+
+
40+
+$p1 = "alice\x99";
41+
+var_dump($stmt->execute(['username' => $p1]));
42+
+
43+
+?>
44+
+--EXPECT--
45+
+bool(false)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
From: Niels Dossche <7771979+ndossche@users.noreply.github.com>
2+
Date: Sun, 9 Nov 2025 13:23:11 +0100
3+
Subject: Fix GHSA-h96m-rvf9-jgm2
4+
5+
(cherry picked from commit 8b801151bd54b36aae4593ed6cfc096e8122b415)
6+
(cherry picked from commit e4516e52979e8b67d9d35dfdbcc5dc7368263fa2)
7+
---
8+
ext/standard/array.c | 7 ++++++-
9+
ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt | 16 ++++++++++++++++
10+
2 files changed, 22 insertions(+), 1 deletion(-)
11+
create mode 100644 ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt
12+
13+
diff --git a/ext/standard/array.c b/ext/standard/array.c
14+
index 09c3a9f..fd92ef0 100644
15+
--- a/ext/standard/array.c
16+
+++ b/ext/standard/array.c
17+
@@ -3778,7 +3778,7 @@ static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETE
18+
} else {
19+
zval *src_entry;
20+
HashTable *src, *dest;
21+
- uint32_t count = 0;
22+
+ uint64_t count = 0;
23+
24+
for (i = 0; i < argc; i++) {
25+
zval *arg = args + i;
26+
@@ -3790,6 +3790,11 @@ static inline void php_array_merge_or_replace_wrapper(INTERNAL_FUNCTION_PARAMETE
27+
count += zend_hash_num_elements(Z_ARRVAL_P(arg));
28+
}
29+
30+
+ if (UNEXPECTED(count >= HT_MAX_SIZE)) {
31+
+ zend_throw_error(NULL, "The total number of elements must be lower than %u", HT_MAX_SIZE);
32+
+ return;
33+
+ }
34+
+
35+
arg = args;
36+
src = Z_ARRVAL_P(arg);
37+
/* copy first array */
38+
diff --git a/ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt b/ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt
39+
new file mode 100644
40+
index 0000000..2e3e853
41+
--- /dev/null
42+
+++ b/ext/standard/tests/array/GHSA-h96m-rvf9-jgm2.phpt
43+
@@ -0,0 +1,16 @@
44+
+--TEST--
45+
+GHSA-h96m-rvf9-jgm2
46+
+--FILE--
47+
+<?php
48+
+
49+
+$power = 20; // Chosen to be well within a memory_limit
50+
+$arr = range(0, 2**$power);
51+
+try {
52+
+ array_merge(...array_fill(0, 2**(32-$power), $arr));
53+
+} catch (Error $e) {
54+
+ echo $e->getMessage(), "\n";
55+
+}
56+
+
57+
+?>
58+
+--EXPECTF--
59+
+The total number of elements must be lower than %d

0 commit comments

Comments
 (0)