Two weeks ago, I noted that I was preparing to switch from PHP 7.0 to 7.1. It took me a bit more time than expected, thanks to a segmentation fault that appeared in 7.1 when using OPcache.
Much of the process follows what I wrote about in 2015 when I initially switched to 7.0. Since then, the only notable change is the addition of the GeoIP module alongside Redis.
Building PHP 7.1
Download the latest source from http://php.net/downloads.php and expand the archive. Next, configure the build; in my case:
./configure --prefix=/usr/local/php7/7.1.0 --with-pdo-pgsql --with-zlib-dir --with-freetype-dir --enable-mbstring --with-libxml-dir=/usr --enable-soap --enable-calendar --with-curl --with-mcrypt --with-zlib --with-gd --with-pgsql --disable-rpath --enable-inline-optimization --with-bz2 --with-zlib --enable-sockets --enable-sysvsem --enable-sysvshm --enable-pcntl --enable-mbregex --enable-exif --enable-bcmath --with-mhash --enable-zip --with-pcre-regex --with-pdo-mysql --with-mysqli --with-mysql-sock=/var/run/mysqld/mysqld.sock --with-jpeg-dir=/usr --with-png-dir=/usr --with-webp-dir --enable-gd-native-ttf --with-openssl --with-fpm-user=www-data --with-fpm-group=www-data --with-libdir=/lib/x86_64-linux-gnu --enable-ftp --with-imap --with-imap-ssl --with-kerberos --with-gettext --with-xmlrpc --with-xsl --enable-opcache --enable-fpm --enable-intl --enable-mysqlnd --with-pear --with-gmp --with-tidy
Note: added --with-gmp
on March 21, 2017 to satisfy a new piece of software’s dependency, while --with-tidy
was added the following day.
The build can now be compiled:
make make install
Building modules
Both Redis and GeoIP are available as PECL modules for 7.1: https://pecl.php.net/package/redis/3.1.0 and https://pecl.php.net/package/geoip/1.1.1. Since I need to maintain PHP 5.6 alongside 7.x, I don’t use the PECL installer; instead, I follow the “Compiling shared PECL extensions with phpize” approach.
Redis
Download the archive from https://pecl.php.net/get/redis and expand it, then prepare and build the module:
cd redis /usr/local/php7/7.1.0/bin/phpize ./configure --prefix=/usr/local/php7/phpredis --with-php-config=/usr/local/php7/7.1.0/bin/php-config --enable-redis make make install
GeoIP
Download the archive from https://pecl.php.net/get/geoip and expand it, then prepare and build the module:
cd geoip /usr/local/php7/7.1.0/bin/phpize ./configure --prefix=/usr/local/php7/geoip --with-php-config=/usr/local/php7/7.1.0/bin/php-config make make install
Switching Versions
As I described in “Hello PHP 7!,” I use a symlink from /usr/local/php7/7.0.0
to point to the latest build in the 7.x family. In hindsight, I should’ve named the symlink simply 7
, or perhaps 7.x
, but I didn’t and I’m loathe to change it now. Symlinks in /usr/local/bin
, in cron entries, and in myriad other places now point to that symlink to invoke PHP 7, so I’ll live with my decision until I migrate to a new server instance.
Before updating the symlink, it’s wise to stop PHP-FPM otherwise you’ll need to manually kill the old PHP 7 processes.
/etc/init.d/php7 stop rm /usr/local/php7/7.0.0 ln -s /usr/local/php7/7.1.0 /usr/local/php7/7.0.0 /etc/init.d/php7 start
Debugging the segfault
Shortly after switching to 7.1, Jetpack’s POST requests to the xmlrpc.php
endpoint began failing with a segmentation fault: child exited on signal 11 (SIGSEGV)
. I left PHP 7.1 in place, as no other requests were impacted, which allowed me to grab a core dump as described at https://bugs.php.net/bugs-generating-backtrace.php. From that, I retrieved the following backtrace:
#0 _zend_hash_add_or_update_i (flag=1, pData=0x7fbaf5361400, key=0x7fbaf5364458, ht=0x7fbaf0299e00) at /usr/local/src/php-7.1.0/Zend/zend_hash.c:600 #1 _zend_hash_update (ht=0x7fbaf0299e00, key=0x7fbaf5364458, pData=0x7fbaf5361400) at /usr/local/src/php-7.1.0/Zend/zend_hash.c:627 #2 0x0000000000901eb1 in ZEND_ADD_ARRAY_ELEMENT_SPEC_CONST_CONST_HANDLER (execute_data=0x7fbaf64157a0) at /usr/local/src/php-7.1.0/Zend/zend_vm_execute.h:5884 #3 0x0000000000903db8 in execute_ex (ex=<optimized out>) at /usr/local/src/php-7.1.0/Zend/zend_vm_execute.h:432 #4 0x000000000095aa10 in zend_execute (op_array=op_array@entry=0x7fbaf646d000, return_value=return_value@entry=0x0) at /usr/local/src/php-7.1.0/Zend/zend_vm_execute.h:474 #5 0x00000000008b7fe4 in zend_execute_scripts (type=type@entry=8, retval=retval@entry=0x0, file_count=file_count@entry=3) at /usr/local/src/php-7.1.0/Zend/zend.c:1474 #6 0x0000000000856640 in php_execute_script (primary_file=primary_file@entry=0x7ffca4bc3680) at /usr/local/src/php-7.1.0/main/main.c:2533 #7 0x0000000000461df6 in main (argc=<optimized out>, argv=<optimized out>) at /usr/local/src/php-7.1.0/sapi/fpm/fpm/fpm_main.c:1967
A bit of Googling and a review of my php7.ini
indicated that OPcache was likely to blame. To confirm this, I disabled OPcache for all PHP 7 requests by modifying php7.ini
, then undid this change after the segfaults stopped. I then modified WordPress Core’s xmlrpc.php
to disable OPcache for requests to it, by adding ini_set( 'opcache.enable', 0 );
to the top of the file; again, I removed this modification after the segfaults stopped.
With the cause confirmed, I modified my nginx configuration to disable OPcache for Core’s XML-RPC requests. My configuration is already modular, which made this rather straightforward. To an existing common include, I added the following:
# PHP 7.1 segfaults with Opcache location = /xmlrpc.php { fastcgi_param PHP_ADMIN_VALUE "opcache.enable=0"; include common/wp-php.conf; }
Since testing and reloading nginx, no segmentation faults have been logged for php-fpm
.
If I can sort out how to reproduce the issue in a self-contained way, I’ll report this to https://bugs.php.net/, but it’s not clear to me what the conflict is, just where it occurred and how to avoid it. Either way, I’ll need to remember to disable the above fix after each subsequent PHP 7.1 release, in case the issue is resolved independently.
https://github.com/Automattic/jetpack/pull/6565, part of Jetpack 4.8, fixes the segfault, allowing Opcache to be restored for XML-RPC requests.
Thanks, that’s very useful. I’ve produced the same backtrace and the same fix works.
& thanks Erick, although I’m slightly puzzled, I’ve got Jetpack 4.9 and still seeing the problem.
Indeed, I spoke a bit too soon: https://github.com/Automattic/jetpack/issues/6106. That I’m no longer experiencing segfaults is due to the particular Jetpack modules I have enabled.