From 66d01e3ea3a4885d679a657eabb6ea9a1cebb87d Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 29 May 2026 21:44:14 +0100 Subject: [PATCH 1/3] ext/openssl: openssl_encrypt() zend mm heap overflow on AES-WRAP-PAD mode. Fix #22186 --- ext/openssl/openssl.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 6d179cebabda..1e5ea67057cd 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -7932,6 +7932,7 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type, const 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(); @@ -7945,7 +7946,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_flags(cipher_type) & EVP_CIPH_MODE) == 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, false); if (!EVP_CipherUpdate(cipher_ctx, (unsigned char*)ZSTR_VAL(*poutbuf), &i, (const unsigned char *)data, (int)data_len)) { @@ -7957,7 +7970,7 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type, } */ php_openssl_store_errors(); - zend_string_release_ex(*poutbuf, 0); + zend_string_release_ex(*poutbuf, false); return FAILURE; } From 6501136bf30329c2d817d2f479d1b59787176dcb Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 29 May 2026 22:11:02 +0100 Subject: [PATCH 2/3] add test --- ext/openssl/tests/gh22186.phpt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 ext/openssl/tests/gh22186.phpt diff --git a/ext/openssl/tests/gh22186.phpt b/ext/openssl/tests/gh22186.phpt new file mode 100644 index 000000000000..a4d1ceae9b90 --- /dev/null +++ b/ext/openssl/tests/gh22186.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-22186 (Heap buffer overflow in openssl_encrypt with AES-WRAP-PAD) +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + +--EXPECT-- +done From c0eecd57d954ea549f8ace887ae44c11a85ee786 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Fri, 29 May 2026 22:28:51 +0100 Subject: [PATCH 3/3] fix test attempt --- ext/openssl/openssl.c | 2 +- ext/openssl/tests/gh22186.phpt | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 1e5ea67057cd..1689662f07a3 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -7947,7 +7947,7 @@ static int php_openssl_cipher_update(const EVP_CIPHER *cipher_type, } #ifdef EVP_CIPH_WRAP_MODE - if ((EVP_CIPHER_flags(cipher_type) & EVP_CIPH_MODE) == 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. diff --git a/ext/openssl/tests/gh22186.phpt b/ext/openssl/tests/gh22186.phpt index a4d1ceae9b90..8f28e6c45b58 100644 --- a/ext/openssl/tests/gh22186.phpt +++ b/ext/openssl/tests/gh22186.phpt @@ -4,8 +4,12 @@ GH-22186 (Heap buffer overflow in openssl_encrypt with AES-WRAP-PAD) openssl --SKIPIF-- --FILE--