php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #73783 SIG_IGN doesn't work when Zend Signals is enabled
Submitted: 2016-12-19 10:25 UTC Modified: 2016-12-29 20:17 UTC
From: zorg at razza dot org Assigned:
Status: Closed Package: PCNTL related
PHP Version: 7.1.0 OS: Ubuntu 16.04.1 LTS
Private report: No CVE-ID: None
 [2016-12-19 10:25 UTC] zorg at razza dot org
Description:
------------
The use of pcntl_signal(SIGCHLD, SIG_IGN) breaks sleep(). In 7.0 the below script would sleep for 60 seconds then exit, in 7.1 the script does not sleep at all.

Test script:
---------------
pcntl_signal(SIGCHLD, SIG_IGN);
sleep(60);
exit('Done');

Expected result:
----------------
Sleep for 60 seconds, print 'Done' and exit.

Actual result:
--------------
Instantly prints 'Done' and exist.

Patches

Pull Requests

Pull requests:

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2016-12-19 10:30 UTC] zorg at razza dot org
Sorry, the test script is incorrect. It should be:

pcntl_signal(SIGCHLD, SIG_IGN);
$pid = @pcntl_fork();
if ($pid == -1)
{
  exit(pcntl_get_last_error() . ': ' . pcntl_strerror(pcntl_get_last_error()));
}
elseif ($pid == 0)
{
  exit();
}

sleep(60);
exit("Done");

This also creates a <defunct> process when pcntl_signal(SIGCHLD, SIG_IGN) should have removed it.
 [2016-12-19 14:00 UTC] cmb@php.net
-Package: *General Issues +Package: PCNTL related
 [2016-12-19 20:54 UTC] dave at mudsite dot com
This bug exists because ZEND_SIGNALS was defaulted to "yes" with PHP 7.1.  The problem with this is that sleep() will sleep for the number of seconds, OR, is interrupted by a signal that is not ignored.

With your test script with pcntl_signal(SIGCHLD, SIG_IGN), you're instructing PHP to ignore the end of a child.  However, ZEND_SIGNALS, zend_sigaction() sets the set handler into the SIGG(handlers), but, will set the actual signal handler to zend_signal_handler_defer, which is not SIG_IGN.  As such, during the sleeping, when a SIGCHLD is raised, sleep bails because it is not "ignored" at the system level, and ends up calling zend_signal_handler_defer which will not process the signal.

I believe a safe fix would be to in zend_sigaction, only set the defer handler IF the incoming sigact's handler is not SIG_IGN.  Will make a PR and see if it's acceptable or not.
 [2016-12-20 08:15 UTC] zorg at razza dot org
Would this also fix the issue where pcntl_signal(SIGCHLD, SIG_IGN) no longer stops <defunct> threads from remaining until the main process has ended? In 7.0 threads would exit cleanly, but since upgrading to 7.1 they all sit as <defunct> now.
 [2016-12-20 15:41 UTC] dave at mudsite dot com
I would assume as much, I wasn't directly able to replicate the <defunct> process. I tried on both my Debian setup & MacOS 10.12, which both lack the <defunct> but do both sleep correctly with the fix in place.
The output I get with PHP 7.1, and my fix are as follows:

dwalker@linux:~/src/php PHP-7.1 › time sapi/cli/php e.php && ps aux | grep defunc
Parent
Child Dying
After Sleep
real    0m0.022s
user    0m0.004s
sys     0m0.016s
dwalker  24037  0.0  0.0  12784   940 pts/3    S+   08:38   0:00 grep --color=auto defunc



dwalker@linux:~/src/php fix-73783 › time sapi/cli/php e.php && ps aux | grep defunc
Parent
Child Dying
After Sleep

real    0m10.015s
user    0m0.012s
sys     0m0.004s
dwalker  16683  0.0  0.0  12784   900 pts/3    S+   08:37   0:00 grep --color=auto defunc
 [2016-12-29 20:17 UTC] nikic@php.net
-Summary: pcntl_signal() issue +Summary: SIG_IGN doesn't work when Zend Signals is enabled
 [2016-12-29 20:19 UTC] nikic@php.net
Automatic comment on behalf of dave@mudsite.com
Revision: http://git.php.net/?p=php-src.git;a=commit;h=b09c2f899ebc14029d0936d770cced10b607f84b
Log: Fixed bug #73783
 [2016-12-29 20:19 UTC] nikic@php.net
-Status: Open +Status: Closed
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Nov 23 08:01:28 2024 UTC