Skip to content

Commit cbc0489

Browse files
devnexeniluuu1994
authored andcommitted
ext/openssl: openssl_encrypt() zend mm heap overflow on AES-WRAP-PAD mode.
Fix #22186 close GH-22187
1 parent 1182b14 commit cbc0489

3 files changed

Lines changed: 50 additions & 2 deletions

File tree

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ PHP NEWS
22
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
33
?? ??? ????, PHP 8.2.32
44

5+
- OpenSSL:
6+
. Fixed bug GH-22187 (Memory corruption (zend_mm_heap corrupted) in
7+
openssl_encrypt with AES-WRAP-PAD). (David Carlier)
58

69
07 May 2026, PHP 8.2.31
710

ext/openssl/openssl.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7396,6 +7396,7 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
73967396
const char *aad, size_t aad_len, int enc) /* {{{ */
73977397
{
73987398
int i = 0;
7399+
size_t outlen = data_len + EVP_CIPHER_block_size(cipher_type);
73997400

74007401
if (mode->is_single_run_aead && !EVP_CipherUpdate(cipher_ctx, NULL, &i, NULL, (int)data_len)) {
74017402
php_openssl_store_errors();
@@ -7409,7 +7410,19 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
74097410
return FAILURE;
74107411
}
74117412

7412-
*poutbuf = zend_string_alloc((int)data_len + EVP_CIPHER_block_size(cipher_type), 0);
7413+
#ifdef EVP_CIPH_WRAP_MODE
7414+
if ((EVP_CIPHER_mode(cipher_type)) == EVP_CIPH_WRAP_MODE) {
7415+
/*
7416+
* RFC 5649 wrap-with-padding rounds the input up to the block size
7417+
* and prepends an integrity block, we reserve one extra block.
7418+
* See EVP_EncryptUpdate(3): wrap mode may write up to
7419+
* inl + cipher_block_size bytes.
7420+
*/
7421+
outlen += EVP_CIPHER_block_size(cipher_type);
7422+
}
7423+
#endif
7424+
7425+
*poutbuf = zend_string_alloc(outlen, false);
74137426

74147427
if (!EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf),
74157428
&i, (const unsigned char *)data, (int)data_len)) {
@@ -7421,7 +7434,7 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
74217434
}
74227435
*/
74237436
php_openssl_store_errors();
7424-
zend_string_release_ex(*poutbuf, 0);
7437+
zend_string_release_ex(*poutbuf, false);
74257438
return FAILURE;
74267439
}
74277440

ext/openssl/tests/gh22186.phpt

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
GH-22186 (Heap buffer overflow in openssl_encrypt with AES-WRAP-PAD)
3+
--EXTENSIONS--
4+
openssl
5+
--SKIPIF--
6+
<?php
7+
/* openssl_get_cipher_methods() enumerates provider ciphers, but openssl_encrypt()
8+
* resolves names via the legacy EVP_get_cipherbyname(), so on some builds the
9+
* cipher is listed yet not usable. Probe the actual call path instead. */
10+
if (!@openssl_encrypt("test", "aes-128-wrap-pad", str_repeat("k", 16),
11+
OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, str_repeat("\0", 4))) {
12+
die('skip aes-128-wrap-pad not usable on this OpenSSL build');
13+
}
14+
?>
15+
--FILE--
16+
<?php
17+
$pass = str_repeat("k", 16);
18+
$iv = str_repeat("\0", 4);
19+
20+
for ($i = 1; $i < 258; $i++) {
21+
$data = str_repeat("a", $i);
22+
$enc = openssl_encrypt($data, 'aes-128-wrap-pad', $pass, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, $iv);
23+
$dec = openssl_decrypt($enc, 'aes-128-wrap-pad', $pass, OPENSSL_RAW_DATA | OPENSSL_DONT_ZERO_PAD_KEY, $iv);
24+
if ($dec !== $data) {
25+
die("mismatch at $i\n");
26+
}
27+
}
28+
29+
echo "done\n";
30+
?>
31+
--EXPECT--
32+
done

0 commit comments

Comments
 (0)