php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80376 last day of the month causes runway cpu usage
Submitted: 2020-11-17 19:38 UTC Modified: 2020-12-21 10:33 UTC
Votes:3
Avg. Score:5.0 ± 0.0
Reproduced:3 of 3 (100.0%)
Same Version:3 (100.0%)
Same OS:3 (100.0%)
From: mav2287 at aol dot com Assigned: derick (profile)
Status: Closed Package: Date/time related
PHP Version: 7.4.12 OS: OS X 10.11.6 El Capitan
Private report: No CVE-ID: None
 [2020-11-17 19:38 UTC] mav2287 at aol dot com
Description:
------------
As discovered and documented via the Mac Ports project ( see ticket url below ) php versions 7.3.23 & 7.4.11 and newer seem to create a process with runaway CPU usage when attempting to get the last day of the month. This runaway process seems to continue indefinitely and consumes 100% cpu.

Orignal Mac Ports Ticket:
https://trac.macports.org/ticket/61351

Test script:
---------------
# php -a
Interactive shell

php > $date = new \DateTime();
php > $newDate = $date->modify('last day of this month');
php > var_dump($newDate);

Expected result:
----------------
creation of a DateTime object and a blinking cursor awaiting new command

Actual result:
--------------
PHP will hang indefinitely and the php process will consume 100% cpu until manually killed

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-11-17 19:41 UTC] mav2287 at aol dot com
In case it isn't clear when using the sample script once you execute "$newDate = $date->modify('last day of this month');" PHP will hang. You will never get the chance to go to the next step of the script and do "php > var_dump($newDate);" as the object is never created,
 [2020-11-17 21:30 UTC] requinix@php.net
-Status: Open +Status: Feedback
 [2020-11-17 21:30 UTC] requinix@php.net
Unless someone else can reproduce this (I couldn't on Linux),

Can you try running PHP with gdb or another debugger attached, run the code to the point it hangs, interrupt PHP so you return to the debugger, and get a backtrace? Hopefully that will help identify what sort of loop is involved.
 [2020-11-22 01:57 UTC] mav2287 at aol dot com
-Status: Feedback +Status: Open
 [2020-11-22 01:57 UTC] mav2287 at aol dot com
Haven't done a debug backtrace like that before and don't have gdb on that system is this something I can do with Xdebug?
 [2020-11-22 05:55 UTC] requinix@php.net
No. Xdebug provides a backtrace of PHP code, but what we need here is a backtrace of PHP's own internals.

I don't know Macs well, but if you can install gdb then using it is pretty simple:
1. Put the PHP code into a file so you can execute it more easily
2. Start gdb by telling it to load PHP (as in the program)
3. Have it `run` PHP, passing an argument of the file name so PHP will run the file
4. Let it run for a couple seconds so that it's definitely hung somewhere
5. Interrupt PHP with... Cmd+C? Or maybe Ctrl+C.
6. Have gdb create a backtrace with the `bt` command

Basically, your terminal window will probably end up looking something like

> gdb /path/to/php
(gdb loads)
> (gdb) run /path/to/file.php
(no output while php hangs)
Cmd+C
> (gdb) bt
(backtrace output)

Take a look at the output, see if it appears like it might be useful, and post it. Ideally you would use a debug build of PHP for this, which includes more information than regular builds do, but with some luck that won't be necessary.

For bonus points, repeat that process a few times, and compare the backtraces to see what is and isn't common between them.
 [2020-11-22 09:03 UTC] mav2287 at aol dot com
I was able to get GDB installed and I was able to get both 7.3 and 7.4 re-compiled with --enable-debug so that I could use GDB. That is when things got interesting or annoying depending on how you look at it. The bug was not present and I couldn't replicate it after compiling with --enable-debug as a sanity check I compiled both again with it and sure enough the bug was back. Any ideas how to get a backtrace since the bug doesn't show up when the --enable-debug flag is set?
 [2020-11-22 10:03 UTC] requinix@php.net
Go ahead and get the backtrace anyway. It might not have as much useful information, but it's better than nothing.
 [2020-12-10 03:26 UTC] mav2287 at aol dot com
Sorry for the delay, things have been busy at work.

I did some additional testing and found the bug present in PHP 8.0 as well. Although I tried everything I could think of I was never able to get a backtrace to run with gdb; it just kept saying the symbols were not present no matter what I did.

I was able to get a compiled version of 7.3, 7.4 and 8.0 without enable-debug that did not have the issue by using clang 10. It would appear that using the default version of clang on that machine ( full details below ) may have been the cause. I don't know what specifically about clang 8 it didn't like though.


Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
 [2020-12-15 13:17 UTC] cmb@php.net
-Status: Open +Status: Feedback -Assigned To: +Assigned To: cmb
 [2020-12-15 13:17 UTC] cmb@php.net
If that hangs only with clang 8 non-debug builds, that may hint at
a bad optimization in that compiler.  Without further information,
this is likely not actionable from our side.
 [2020-12-16 14:10 UTC] ekl at woodwing dot com
I can repro with PHP 7.3.25 on our MacOSX 10.11.6 MacMini as follows:

$ sudo port install php73
$ sudo port install php73 +debug

$ sudo port activate php73
The following versions of php73 are currently installed:
 1) php73 @7.3.25_2+debug+libedit
 2) php73 @7.3.25_2+libedit (active)
Enter a number to select an option: 1
$ php73 -r 'print (new DateTime( "15 Dec 2020" ))->format("Ymd H:i:s").PHP_EOL;'
20201215 00:00:00

$ php73 -v
PHP 7.3.25 (cli) (built: Dec 16 2020 14:43:06) ( NTS DEBUG )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.25, Copyright (c) 1998-2018 Zend Technologies

$ sudo port activate php73
The following versions of php73 are currently installed:
 1) php73 @7.3.25_2+debug+libedit
 2) php73 @7.3.25_2+libedit (active)
Enter a number to select an option: 2
$ php73 -r 'print (new DateTime( "15 Dec 2020" ))->format("Ymd H:i:s").PHP_EOL;'
===>>> HANGS! <<<===

$ php73 -v
PHP 7.3.25 (cli) (built: Dec  6 2020 20:04:26) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.25, Copyright (c) 1998-2018 Zend Technologies

Notes:
- This problem suddenly occurred after upgrading from an older PHP 7.3 patch version. (I can't remember the patch level that was still working. I assume it was something close to PHP 7.3.10, like one year old.)
- This problem is a major show-stopper for us. We can't go back to an older PHP 7.3 patch level anymore, as we thought it was smart to clear older installations first, once we ran into this problem.
- Exactly the same problem happens for PHP 7.4.13 installed on the same machine.
- This problem does NOT happen on my MacOSX 10.13.6 MacBook Pro.
 [2020-12-16 14:13 UTC] cmb@php.net
-Status: Feedback +Status: Open -Assigned To: cmb +Assigned To:
 [2020-12-16 14:13 UTC] cmb@php.net
Thanks!  I think somebody with access to macOS should have a closer look.
 [2020-12-16 17:32 UTC] ekl at woodwing dot com
On the problematic 10.11.6 machine, after uninstalling PHP 7.3.25, I managed to bring back the old PHP 7.3.10 installation as follows:

$ git clone --single-branch https://github.com/macports/macports-ports.git
$ git log --grep "php73 to 7.3.10"

commit 22d4d0a0ba24b20946696eeb887b02e2d15509d5
Author: Ryan Schmidt <ryandesign@macports.org>
Date:   Thu Sep 26 19:31:04 2019 -0500

    php: Update php73 to 7.3.10

=> Note down the commit number for usage below.

$ cd /Users/your-user-name
$ git checkout 22d4d0a0ba24b20946696eeb887b02e2d15509d5
$ portindex /Users/your-user-name/macports-ports

$ vi /opt/local/etc/macports/sources.conf
=> Add the following line just before the rsync command:
	file:///Users/your-user-name/macports-ports [nosync]

$ sudo port install php73 @7.3.10
$ php73 -v
PHP 7.3.10 (cli) (built: Dec 16 2020 17:51:56) ( NTS )
Copyright (c) 1997-2018 The PHP Group

The following test shows that the old PHP 7.3.10 installation has NO problem:

$ php73 -r 'print (new DateTime( "15 Dec 2020" ))->format("Ymd H:i:s").PHP_EOL;'
20201215 00:00:00
 [2020-12-17 10:54 UTC] ekl at woodwing dot com
I did some more digging on the problematic MacOSX 10.11.6 machine. 

It seems that between end of March 2020 and early October 2020 there were no PHP 7.3 nor PHP 7.4 patches released by MacPorts:

--------------------------------------------------------

$ git log --grep "php73"

commit 2b90e9dbf0b7c7d330325e724793d1ab6fc0e062
Author: Ryan Schmidt <ryandesign@macports.org>
Date:   Fri Oct 2 09:59:49 2020 -0500

    php: Update php73 to 7.3.23


commit b03fe0675f1efe520c67b13a6ccc17a6f9a99e09
Author: Ryan Schmidt <ryandesign@macports.org>
Date:   Tue Mar 24 07:24:35 2020 -0500

    php: Update php73 to 7.3.16

--------------------------------------------------------

$ git log --grep "php74"

commit 2eca46bc0c123000c2ef7efddfd382b411d41dfc
Author: Ryan Schmidt <ryandesign@macports.org>
Date:   Thu Oct 1 18:23:45 2020 -0500

    php: Update php74 to 7.4.11


commit 1b2546f94ff2583e3727f7f2df19f72386afc22e
Author: Ryan Schmidt <ryandesign@macports.org>
Date:   Thu Mar 26 09:30:28 2020 -0500

    php: Update php74 to 7.4.4

--------------------------------------------------------

I repeated my test (I mentioned in earlier posts) for all these 4 commits listed above. And it turns out that:

- 7.3.16 works!
- 7.3.23 hangs!

- 7.4.4 works!
- 7.4.11 hangs!

I hope this helps you pinpointing the problem.
 [2020-12-19 10:24 UTC] php-comment-2020 at ryandesign dot com
In the MacPorts bug report I suggested that it might have to do with the version of the compiler. mav2287 confirmed that compiling the affected php versions with Apple Clang 800.0.42.1 from Xcode 8.2.1 (the last version compatible with OS X 10.11) produces a php that does not work, while recompiling with open-source clang 10.0.1 installed by MacPorts produces a php that works. For those who want to try this, you would use e.g.:

sudo port install clang-10
sudo port -ns upgrade --force php80 configure.compiler=macports-clang-10

This would only recompile the command line php80. Repeat the procedure for php80-apache2handler, php80-cgi, and/or php80-fpm if you use those SAPIs. Substitute php74, php73, or php72 as needed. We now have clang-11 in MacPorts so you could presumably use that, or clang-9.0, instead, if desired.

So this suggests that there is some code that was added to php this year that does not get along with at least Apple Clang 800.0.42.1, though it's not clear which range of Apple Clang versions are affected.

Perhaps I can git bisect to find which commit introduced the problem.
 [2020-12-19 11:52 UTC] evtukhov08 at gmail dot com
PHP 8 have the same issue with dates on Mac OS 10.11 installed from brew.
 [2020-12-19 12:14 UTC] php-comment-2020 at ryandesign dot com
Thanks to git bisect, I was able to determine that the problem was introduced in this commit:

https://github.com/php/php-src/commit/778902db63b08a5bdcb45287b1e8c6ef5ef60932

(Update timelib to 2018.04)

Unfortunately that's a large commit, so I guess next I need to bisect timelib to find out what commit between timelib 2018.03 and 2018.04 caused this.
 [2020-12-19 13:52 UTC] php-bugs-2020 at ryandesign dot com
The problem is not due to a change in timelib; it's due to the version of re2c that was used to generate timelib's parse_date.c and parse_iso_intervals.c when it was added to php. When timelib 2018.03 was added to php, re2c 0.15.3 was used. When 2018.04 was added to php, re2c 2.0.3 was used. If I regenerate with re2c 0.15.3, then I get a working php, even with timelib 2018.04. If I regenerate with re2c 0.16 or later, then I get a non-working php.

So the simplest fix for now which I recommend the php developers use is to regenerate with re2c 0.15.3.

It would require more investigation to determine whether this is a bug in re2c 0.16 or later or a bug in how timelib is using re2c. The changelog entry for re2c 0.16 suggests that there have been dramatic changes to how the c files are generated and that the correctness of the changes could not be verified manually, so it seems possible that the automatic verification they used wasn't fully correct or did not cover all cases.

http://re2c.org/releases/release_notes.html#release-0-16
 [2020-12-19 14:39 UTC] php-bugs-2020 at ryandesign dot com
This problem has bitten PHP before. See this re2c bug report from 2016 after 0.16 was released and PHP regenerated timelib with it:

https://github.com/skvadrik/re2c/issues/154

There, the developer of re2c says it is a bug in clang which evidently was fixed at some point. That report was with Apple LLVM version 7.3.0 (clang-703.0.29), this issue now is with Apple LLVM version 8.0.0 (clang-800.0.42.1), and we do not see the problem with Apple LLVM version 9.0.0 (clang-900.0.39.2).

PHP previously fixed it by regenerating with re2c 0.13.5:

https://github.com/php/php-src/commit/90c26fb6b102d68a90a519eacec095265d6ba100

and then a few days later regenerating with 0.15.3:

https://github.com/php/php-src/commit/da3995852edf1a5b7b4c8bda7a1e804cb263dc5e
 [2020-12-19 15:41 UTC] php-bugs-2020 at ryandesign dot com
After installing and testing several versions of Apple Clang, I've found that the broken versions are Apple LLVM version 7.3.0 (clang-703.0.29) (which came with Xcode 7.3) through Apple LLVM version 8.0.0 (clang-800.0.42.1) (which came with Xcode 8.1/8.2/8.2.1) inclusive.

Apple LLVM version 7.0.2 (clang-700.1.81) (which came with Xcode 7.2.1) and earlier work fine, and Apple LLVM version 8.1.0 (clang-802.0.38) (which came with Xcode 8.3) and later work fine.

There are several other parsers in php that were generated by re2c 0.16 or later. I wonder if any of those functions might also be affected by hangs when compiled by an affected compiler.

./Zend/zend_ini_scanner.c:/* Generated by re2c 1.0.1 */
./Zend/zend_ini_scanner_defs.h:/* Generated by re2c 1.0.1 */
./Zend/zend_language_scanner.c:/* Generated by re2c 1.0.1 */
./Zend/zend_language_scanner_defs.h:/* Generated by re2c 1.0.1 */
./ext/date/lib/parse_date.c:/* Generated by re2c 2.0.3 on Mon Aug 31 12:21:15 2020 */
./ext/date/lib/parse_iso_intervals.c:/* Generated by re2c 2.0.3 on Mon Aug 31 12:21:19 2020 */
./ext/json/json_scanner.c:/* Generated by re2c 1.0.1 */
./ext/json/php_json_scanner_defs.h:/* Generated by re2c 1.0.1 */
./ext/standard/url_scanner_ex.c:/* Generated by re2c 1.0.1 */
./ext/standard/var_unserializer.c:/* Generated by re2c 1.0.1 */
./sapi/phpdbg/phpdbg_lexer.c:/* Generated by re2c 1.0.1 */
 [2020-12-21 10:32 UTC] derick@php.net
Automatic comment on behalf of github@derickrethans.nl
Revision: http://git.php.net/?p=php-src.git;a=commit;h=b043759cb42b33e02359ca1c30d8d5b68d8a015e
Log: Fixed bug #80376 (last day of the month causes runway cpu usage)
 [2020-12-21 10:32 UTC] derick@php.net
-Status: Open +Status: Closed
 [2020-12-21 10:32 UTC] derick@php.net
Automatic comment on behalf of github@derickrethans.nl
Revision: http://git.php.net/?p=php-src.git;a=commit;h=288332077fa22b78afda1951a357444a901a9da9
Log: Fixed bug #80376 (last day of the month causes runway cpu usage)
 [2020-12-21 10:33 UTC] derick@php.net
The fix for this bug has been committed.
If you are still experiencing this bug, try to check out latest source from https://github.com/php/php-src and re-test.
Thank you for the report, and for helping us make PHP better.

I've regenerated the parsers for ext/date.
 [2020-12-21 10:33 UTC] derick@php.net
-Assigned To: +Assigned To: derick
 
PHP Copyright © 2001-2021 The PHP Group
All rights reserved.
Last updated: Sun Oct 17 01:03:35 2021 UTC