summaryrefslogtreecommitdiffstats
path: root/php-gh22187.patch
blob: f90a80176e3e694559d0197276e719a0cde1d1e8 (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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
From 5c2cc46b45cf286666671df14edf25fc23676cd8 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)
(cherry picked from commit 09cccab30d53614bb826e4390ad23ad7451b6d6c)
(cherry picked from commit f15d1e26160a8175474160907eb6ab7e10090fa0)
(cherry picked from commit 58c39c2f8402261fd4e8ffd327c37adc53a9c861)
(cherry picked from commit 4eeb25a7ae8f91e517b760423b50a5a5ef9e98fb)
---
 NEWS                           |  6 ++++++
 ext/openssl/openssl.c          | 22 ++++++++++++++++++++++
 ext/openssl/tests/gh22186.phpt | 32 ++++++++++++++++++++++++++++++++
 3 files changed, 60 insertions(+)
 create mode 100644 ext/openssl/tests/gh22186.phpt

diff --git a/NEWS b/NEWS
index d8ae595068..1f7e3dfd8c 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 eda68f2ff1..b6a79249b0 100644
--- a/ext/openssl/openssl.c
+++ b/ext/openssl/openssl.c
@@ -5422,6 +5422,17 @@ PHP_FUNCTION(openssl_encrypt)
 	free_iv = php_openssl_validate_iv(&iv, &iv_len, max_iv_len TSRMLS_CC);
 
 	outlen = data_len + EVP_CIPHER_block_size(cipher_type);
+#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
 	outbuf = safe_emalloc(outlen, 1, 1);
 
 	EVP_EncryptInit(cipher_ctx, cipher_type, NULL, NULL);
@@ -5526,6 +5537,17 @@ PHP_FUNCTION(openssl_decrypt)
 	free_iv = php_openssl_validate_iv(&iv, &iv_len, EVP_CIPHER_iv_length(cipher_type) TSRMLS_CC);
 
 	outlen = data_len + EVP_CIPHER_block_size(cipher_type);
+#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
 	outbuf = emalloc(outlen + 1);
 
 	EVP_DecryptInit(cipher_ctx, cipher_type, NULL, NULL);
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.43.7