From d378dce3e9c7384e6c1d1492175ca02f16ed8409 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Wed, 22 Oct 2025 07:18:23 +0100 Subject: [PATCH 1/2] Fix GH-20257: heap overflow on empty message in `lf` mode. close GH-20258 --- NEWS | 4 +++ ext/standard/mail.c | 42 +++++++++++++++------------- ext/standard/tests/mail/gh20257.phpt | 17 +++++++++++ 3 files changed, 43 insertions(+), 20 deletions(-) create mode 100644 ext/standard/tests/mail/gh20257.phpt diff --git a/NEWS b/NEWS index 8a02683d2cfd6..bb7ec294d2b2e 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,10 @@ PHP NEWS . Fixed bug GH-20217 (ReflectionClass::isIterable() incorrectly returns true for classes with property hooks). (alexandre-daubois) +- Standard: + . Fixed bug GH-20257 (mail() heap overflow with an empty message in lf mode). + (David Carlier) + 23 Oct 2025, PHP 8.5.0RC3 - Core: diff --git a/ext/standard/mail.c b/ext/standard/mail.c index f6161782bdd76..8c8b4e6717e0b 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -614,34 +614,36 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c size_t msg_len = strlen(message); size_t new_len = 0; - for (size_t i = 0; i < msg_len - 1; ++i) { - if (message[i] == '\r' && message[i + 1] == '\n') { - ++new_len; + if (msg_len > 0) { + for (size_t i = 0; i < msg_len - 1; ++i) { + if (message[i] == '\r' && message[i + 1] == '\n') { + ++new_len; + } } - } - if (new_len == 0) { - fprintf(sendmail, "%s", message); - } else { - converted_message = emalloc(msg_len - new_len + 1); - size_t j = 0; - for (size_t i = 0; i < msg_len; ++i) { - if (i < msg_len - 1 && message[i] == '\r' && message[i + 1] == '\n') { - converted_message[j++] = '\n'; - ++i; /* skip LF part */ - } else { - converted_message[j++] = message[i]; + if (new_len == 0) { + fprintf(sendmail, "%s", message); + } else { + converted_message = emalloc(msg_len - new_len + 1); + size_t j = 0; + for (size_t i = 0; i < msg_len; ++i) { + if (i < msg_len - 1 && message[i] == '\r' && message[i + 1] == '\n') { + converted_message[j++] = '\n'; + ++i; /* skip LF part */ + } else { + converted_message[j++] = message[i]; + } } - } - converted_message[j] = '\0'; - fprintf(sendmail, "%s", converted_message); - efree(converted_message); + converted_message[j] = '\0'; + fprintf(sendmail, "%s", converted_message); + efree(converted_message); + } } } else { fprintf(sendmail, "%s", message); } - + fprintf(sendmail, "%s", line_sep); #ifdef PHP_WIN32 ret = pclose(sendmail); diff --git a/ext/standard/tests/mail/gh20257.phpt b/ext/standard/tests/mail/gh20257.phpt new file mode 100644 index 0000000000000..68374173413fc --- /dev/null +++ b/ext/standard/tests/mail/gh20257.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-20257: heap overflow with empty message and mail.cr_lf_mode=lf set +--INI-- +sendmail_path="exit 1" +mail.cr_lf_mode=lf +--CREDITS-- +YuanchengJiang +--FILE-- + +--EXPECTF-- + +Warning: mail(): Sendmail exited with non-zero exit code 1 in %s on line %d +bool(false) From f8656fae35d90f89f2cef6a32c7173aa0c6b27a8 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Wed, 22 Oct 2025 22:56:48 +0100 Subject: [PATCH 2/2] Zend: use uint32_t type instead of int for extended_value counter --- Zend/zend_execute.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index c8863a4b27ad5..69665c5bbbb1f 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -4935,7 +4935,7 @@ static void cleanup_live_vars(zend_execute_data *execute_data, uint32_t op_num, if (last->opcode == ZEND_ROPE_INIT) { zend_string_release_ex(*rope, 0); } else { - int j = last->extended_value; + uint32_t j = last->extended_value; do { zend_string_release_ex(rope[j], 0); } while (j--);