From 5544c0772f21210450f8557d4a7557489933668a Mon Sep 17 00:00:00 2001 From: Jesse Hathaway Date: Tue, 3 Jun 2025 10:03:10 -0500 Subject: [PATCH 1/2] mail: fix exit code handling of sendmail cmd Prior to this commit the return code of the pclose function was assumed to be the exit code of the process. However, the returned value as specified in wait(2) is a bit packed integer and must be interpreted with the provided macros. This has no effect in success cases as the integer is still zero, but in failure cases the wrong value is used, since the 8 least significant bits contain the status code. After this commit we use the macros to obtain the status code, which fixes the EX_TEMPFAIL conditional. For WIN32 the TSRM popen_ex and pclose function are used. The return value of TSRM's pclose is not bit packed so we only check if the return value is non-zero, which should solve, #43327, https://bugs.php.net/bug.php?id=43327 --- ext/standard/mail.c | 32 +++++++++++++++++--- ext/standard/tests/mail/mail_variation3.phpt | 22 ++++++++++++++ ext/standard/tests/mail/mail_variation4.phpt | 22 ++++++++++++++ ext/standard/tests/mail/mail_variation5.phpt | 22 ++++++++++++++ 4 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 ext/standard/tests/mail/mail_variation3.phpt create mode 100644 ext/standard/tests/mail/mail_variation4.phpt create mode 100644 ext/standard/tests/mail/mail_variation5.phpt diff --git a/ext/standard/mail.c b/ext/standard/mail.c index 0243ec897a05e..ff0ec13500946 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -25,6 +25,10 @@ #include "ext/date/php_date.h" #include "zend_smart_str.h" +#ifdef HAVE_SYS_WAIT_H +#include +#endif + #ifdef HAVE_SYSEXITS_H # include #endif @@ -562,6 +566,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c } if (sendmail) { + int ret; #ifndef PHP_WIN32 if (EACCES == errno) { php_error_docref(NULL, E_WARNING, "Permission denied: unable to execute shell to run mail delivery binary '%s'", sendmail_path); @@ -582,24 +587,41 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c fprintf(sendmail, "%s%s", hdr, line_sep); } fprintf(sendmail, "%s%s%s", line_sep, message, line_sep); - int ret = pclose(sendmail); +#ifdef PHP_WIN32 + ret = pclose(sendmail); #if PHP_SIGCHILD if (sig_handler) { signal(SIGCHLD, sig_handler); } #endif - -#ifdef PHP_WIN32 - if (ret == -1) #else + int wstatus = pclose(sendmail); +#if PHP_SIGCHILD + if (sig_handler) { + signal(SIGCHLD, sig_handler); + } +#endif + /* Determine the wait(2) exit status */ + if (wstatus == -1) { + MAIL_RET(false); + } else if (WIFSIGNALED(wstatus)) { + MAIL_RET(false); + } else { + if (WIFEXITED(wstatus)) { + ret = WEXITSTATUS(wstatus); + } else { + MAIL_RET(false); + } + } +#endif + #if defined(EX_TEMPFAIL) if ((ret != EX_OK)&&(ret != EX_TEMPFAIL)) #elif defined(EX_OK) if (ret != EX_OK) #else if (ret != 0) -#endif #endif { MAIL_RET(false); diff --git a/ext/standard/tests/mail/mail_variation3.phpt b/ext/standard/tests/mail/mail_variation3.phpt new file mode 100644 index 0000000000000..03908242d88e9 --- /dev/null +++ b/ext/standard/tests/mail/mail_variation3.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test mail() function : variation sendmail temp fail +--INI-- +sendmail_path=exit 75 +--SKIPIF-- + +--FILE-- + +--EXPECT-- +*** Testing mail() : variation *** +bool(true) diff --git a/ext/standard/tests/mail/mail_variation4.phpt b/ext/standard/tests/mail/mail_variation4.phpt new file mode 100644 index 0000000000000..8b3b301ac0be4 --- /dev/null +++ b/ext/standard/tests/mail/mail_variation4.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test mail() function : variation sigterm +--INI-- +sendmail_path="kill \$\$" +--SKIPIF-- + +--FILE-- + +--EXPECT-- +*** Testing mail() : variation *** +bool(false) diff --git a/ext/standard/tests/mail/mail_variation5.phpt b/ext/standard/tests/mail/mail_variation5.phpt new file mode 100644 index 0000000000000..a6c25feba5e1d --- /dev/null +++ b/ext/standard/tests/mail/mail_variation5.phpt @@ -0,0 +1,22 @@ +--TEST-- +Test mail() function : variation non-zero exit +--INI-- +sendmail_path="exit 123" +--SKIPIF-- + +--FILE-- + +--EXPECT-- +*** Testing mail() : variation *** +bool(false) From 1461beeddefee7facacb0008945707eec92bd000 Mon Sep 17 00:00:00 2001 From: Jesse Hathaway Date: Tue, 3 Jun 2025 10:03:10 -0500 Subject: [PATCH 2/2] mail: add logging on errors Prior to this commit the exit code of the sendmail command, called by the mail function was lost, since the mail function only returns true or false. Add additional logging to the mail function to capture the exit code when the sendmail command fails. --- ext/standard/mail.c | 4 ++++ ext/standard/tests/mail/gh10990.phpt | 3 ++- ext/standard/tests/mail/mail_basic5.phpt | 4 +++- ext/standard/tests/mail/mail_variation1.phpt | 4 +++- ext/standard/tests/mail/mail_variation4.phpt | 4 +++- ext/standard/tests/mail/mail_variation5.phpt | 4 +++- 6 files changed, 18 insertions(+), 5 deletions(-) diff --git a/ext/standard/mail.c b/ext/standard/mail.c index ff0ec13500946..41e2a02078e78 100644 --- a/ext/standard/mail.c +++ b/ext/standard/mail.c @@ -604,13 +604,16 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c #endif /* Determine the wait(2) exit status */ if (wstatus == -1) { + php_error_docref(NULL, E_WARNING, "Sendmail pclose failed %d (%s)", errno, strerror(errno)); MAIL_RET(false); } else if (WIFSIGNALED(wstatus)) { + php_error_docref(NULL, E_WARNING, "Sendmail killed by signal %d (%s)", WTERMSIG(wstatus), strsignal(WTERMSIG(wstatus))); MAIL_RET(false); } else { if (WIFEXITED(wstatus)) { ret = WEXITSTATUS(wstatus); } else { + php_error_docref(NULL, E_WARNING, "Sendmail did not exit"); MAIL_RET(false); } } @@ -624,6 +627,7 @@ PHPAPI bool php_mail(const char *to, const char *subject, const char *message, c if (ret != 0) #endif { + php_error_docref(NULL, E_WARNING, "Sendmail exited with non-zero exit code %d", ret); MAIL_RET(false); } else { MAIL_RET(true); diff --git a/ext/standard/tests/mail/gh10990.phpt b/ext/standard/tests/mail/gh10990.phpt index 4f74c17c22bda..ee28f30313858 100644 --- a/ext/standard/tests/mail/gh10990.phpt +++ b/ext/standard/tests/mail/gh10990.phpt @@ -13,5 +13,6 @@ $from = 'test@example.com'; $headers = ['From' => &$from]; var_dump(mail('test@example.com', 'Test', 'Test', $headers)); ?> ---EXPECT-- +--EXPECTF-- +Warning: mail(): Sendmail exited with non-zero exit code 127 in %sgh10990.php on line %d bool(false) diff --git a/ext/standard/tests/mail/mail_basic5.phpt b/ext/standard/tests/mail/mail_basic5.phpt index b427fbeb670a0..73be1e1eaa1ad 100644 --- a/ext/standard/tests/mail/mail_basic5.phpt +++ b/ext/standard/tests/mail/mail_basic5.phpt @@ -20,7 +20,9 @@ $message = 'A Message'; echo "-- failure --\n"; var_dump( mail($to, $subject, $message) ); ?> ---EXPECT-- +--EXPECTF-- *** Testing mail() : basic functionality *** -- failure -- + +Warning: mail(): Sendmail exited with non-zero exit code 1 in %smail_basic5.php on line %d bool(false) diff --git a/ext/standard/tests/mail/mail_variation1.phpt b/ext/standard/tests/mail/mail_variation1.phpt index 75adc62822358..16779bcf455ee 100644 --- a/ext/standard/tests/mail/mail_variation1.phpt +++ b/ext/standard/tests/mail/mail_variation1.phpt @@ -17,6 +17,8 @@ $subject = 'Test Subject'; $message = 'A Message'; var_dump( mail($to, $subject, $message) ); ?> ---EXPECT-- +--EXPECTF-- *** Testing mail() : variation *** + +Warning: mail(): Sendmail exited with non-zero exit code 127 in %smail_variation1.php on line %d bool(false) diff --git a/ext/standard/tests/mail/mail_variation4.phpt b/ext/standard/tests/mail/mail_variation4.phpt index 8b3b301ac0be4..c761e3bdbd199 100644 --- a/ext/standard/tests/mail/mail_variation4.phpt +++ b/ext/standard/tests/mail/mail_variation4.phpt @@ -17,6 +17,8 @@ $subject = 'Test Subject'; $message = 'A Message'; var_dump(mail($to, $subject, $message)); ?> ---EXPECT-- +--EXPECTF-- *** Testing mail() : variation *** + +Warning: mail(): Sendmail killed by signal %d (%s) in %smail_variation4.php on line %d bool(false) diff --git a/ext/standard/tests/mail/mail_variation5.phpt b/ext/standard/tests/mail/mail_variation5.phpt index a6c25feba5e1d..9e430f40c5dfc 100644 --- a/ext/standard/tests/mail/mail_variation5.phpt +++ b/ext/standard/tests/mail/mail_variation5.phpt @@ -17,6 +17,8 @@ $subject = 'Test Subject'; $message = 'A Message'; var_dump(mail($to, $subject, $message)); ?> ---EXPECT-- +--EXPECTF-- *** Testing mail() : variation *** + +Warning: mail(): Sendmail exited with non-zero exit code 123 in %smail_variation5.php on line %d bool(false)