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
108
109
110
111
112
|
From 09cccab30d53614bb826e4390ad23ad7451b6d6c Mon Sep 17 00:00:00 2001
From: David Carlier <devnexen@gmail.com>
Date: Fri, 29 May 2026 21:44:14 +0100
Subject: [PATCH] ext/openssl: openssl_encrypt() zend mm heap overflow on
AES-WRAP-PAD mode.
Fix #22186
close GH-22187
(cherry picked from commit cbc0489126a7682796aad1e5fb4e51de74af162c)
(cherry picked from commit 95e9851111d249e43948b76663cff1baeb5e758d)
(cherry picked from commit 2a73e91a9f9136fbbfcc9177573b6af71e3d5dce)
(cherry picked from commit e058b01e1bd23421a425cffae9f458b0fa8db222)
---
NEWS | 6 ++++++
ext/openssl/openssl.c | 15 ++++++++++++++-
ext/openssl/tests/gh22186.phpt | 32 ++++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+), 1 deletion(-)
create mode 100644 ext/openssl/tests/gh22186.phpt
diff --git a/NEWS b/NEWS
index fe425c4f5c..fa18e93cb3 100644
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,12 @@
PHP NEWS
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+Backported from 8.2.32
+
+- OpenSSL:
+ . Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in
+ openssl_encrypt with AES-WRAP-PAD). (David Carlier)
+
Backported from 8.2.31
- FPM:
diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c
index 2dc8eb3333..da9da1454e 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -6566,6 +6566,7 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
char *aad, size_t aad_len, int enc) /* {{{ */
{
int i = 0;
+ size_t outlen = data_len + EVP_CIPHER_block_size(cipher_type);
if (mode->is_single_run_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) {
php_openssl_store_errors();
@@ -6579,7 +6580,19 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
return FAILURE;
}
- *poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0);
+#ifdef EVP_CIPH_WRAP_MODE
+ if ((EVP_CIPHER_mode(cipher_type)) == EVP_CIPH_WRAP_MODE) {
+ /*
+ * RFC 5649 wrap-with-padding rounds the input up to the block size
+ * and prepends an integrity block, we reserve one extra block.
+ * See EVP_EncryptUpdate(3): wrap mode may write up to
+ * inl + cipher_block_size bytes.
+ */
+ outlen += EVP_CIPHER_block_size(cipher_type);
+ }
+#endif
+
+ *poutbuf = zend_string_alloc(outlen, 0);
if (!EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf),
&i, (unsigned char *)data, (int)data_len)) {
diff --git a/ext/openssl/tests/gh22186.phpt b/ext/openssl/tests/gh22186.phpt
new file mode 100644
index 0000000000..8f28e6c45b
--- /dev/null
+++ b/ext/openssl/tests/gh22186.phpt
@@ -0,0 +1,32 @@
+--TEST--
+GH-22186 (Heap buffer overflow in openssl_encrypt with AES-WRAP-PAD)
+--EXTENSIONS--
+openssl
+--SKIPIF--
+<?php
+/* openssl_get_cipher_methods() enumerates provider ciphers, but openssl_encrypt()
+ * resolves names via the legacy EVP_get_cipherbyname(), so on some builds the
+ * cipher is listed yet not usable. Probe the actual call path instead. */
+if (!@openssl_encrypt("test", "aes-128-wrap-pad", str_repeat("k", 16),
+ OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, str_repeat("\0", 4))) {
+ die('skip aes-128-wrap-pad not usable on this OpenSSL build');
+}
+?>
+--FILE--
+<?php
+$pass = str_repeat("k", 16);
+$iv = str_repeat("\0", 4);
+
+for ($i = 1; $i < 258; $i++) {
+ $data = str_repeat("a", $i);
+ $enc = openssl_encrypt($data, 'aes-128-wrap-pad', $pass, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, $iv);
+ $dec = openssl_decrypt($enc, 'aes-128-wrap-pad', $pass, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, $iv);
+ if ($dec !== $data) {
+ die("mismatch at $i\n");
+ }
+}
+
+echo "done\n";
+?>
+--EXPECT--
+done
--
2.54.0
|