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
This commit is contained in:
Jesse Hathaway 2025-06-03 10:03:10 -05:00 committed by Jakub Zelenka
parent d6fc7430b9
commit c5e7490963
No known key found for this signature in database
GPG key ID: 1C0779DC5C0A9DE4
4 changed files with 93 additions and 5 deletions

View file

@ -25,6 +25,10 @@
#include "ext/date/php_date.h"
#include "zend_smart_str.h"
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_SYSEXITS_H
# include <sysexits.h>
#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);

View file

@ -0,0 +1,22 @@
--TEST--
Test mail() function : variation sendmail temp fail
--INI--
sendmail_path=exit 75
--SKIPIF--
<?php
if (substr(PHP_OS, 0, 3) == "WIN")
die("skip Won't run on Windows");
?>
--FILE--
<?php
echo "*** Testing mail() : variation ***\n";
// Initialise all required variables
$to = 'user@example.com';
$subject = 'Test Subject';
$message = 'A Message';
var_dump(mail($to, $subject, $message));
?>
--EXPECT--
*** Testing mail() : variation ***
bool(true)

View file

@ -0,0 +1,22 @@
--TEST--
Test mail() function : variation sigterm
--INI--
sendmail_path="kill \$\$"
--SKIPIF--
<?php
if (substr(PHP_OS, 0, 3) == "WIN")
die("skip Won't run on Windows");
?>
--FILE--
<?php
echo "*** Testing mail() : variation ***\n";
// Initialise all required variables
$to = 'user@example.com';
$subject = 'Test Subject';
$message = 'A Message';
var_dump(mail($to, $subject, $message));
?>
--EXPECT--
*** Testing mail() : variation ***
bool(false)

View file

@ -0,0 +1,22 @@
--TEST--
Test mail() function : variation non-zero exit
--INI--
sendmail_path="exit 123"
--SKIPIF--
<?php
if (substr(PHP_OS, 0, 3) == "WIN")
die("skip Won't run on Windows");
?>
--FILE--
<?php
echo "*** Testing mail() : variation ***\n";
// Initialise all required variables
$to = 'user@example.com';
$subject = 'Test Subject';
$message = 'A Message';
var_dump(mail($to, $subject, $message));
?>
--EXPECT--
*** Testing mail() : variation ***
bool(false)