PHP is failing with SIGSEGV
It could be not a common situation, but sometimes you start your PHP container and at some point PHP-FPM is failing showing an error log like:
NOTICE: fpm is running, pid 1
NOTICE: ready to handle connections
WARNING: [pool www] child 7 exited on signal 11 (SIGSEGV - core dumped) after 245.799183 seconds from start
It is a Segmentation Fault. There could be a various reasons for that. The cause of that could be an incompatibility of some PHP Extensions, or even a PHP code executing in some specific edge cases.
Core Dump
To understand the real root cause of SIGSEGV is to backtrace the core dump with GDB and/or Valgrind.
Just to note, this article is not about how to use GDB/Valgrind to find why segfault happens, but how to prepare for it.
Problem #1
In my case, there was no core dump, nowhere. Probably, you may have the same case.
To check where core dump should be saved you could runt he command:
cat /proc/sys/kernel/core_pattern
In my case the output was:
|/usr/share/apport/apport -p%p -s%s -c%c -d%d -P%P -u%u -g%g -- %E
So it means that core dump is piped to apport, but for some reason it's not resulted in a core dump saved to a file.
I am using php8.2-fpm-alpine3.18 (now) and php8.0-alpine3.12 (before) Docker images. But it seems majority (if not all) Alpine images has the same problem.
Solution #1
You need to do a few changes to let Docker image properly save the core dump.
-
In your Docker entry point file, please add the following lines before the last
execstatement:
ulimit -c unlimited echo '/tmp/core' > /proc/sys/kernel/core_pattern echo 1 > /proc/sys/fs/suid_dumpable -
In the
docker-compose.ymlfind your PHP service and update the file so it will look like this (of course, omit >>> and <<<):
services: php-app: ... >>> privileged: true ulimits: core: 99999999999 <<< ... Rebuild the image.
Now, when segfault will happen you should be able to find it in
/tmpdirectory. A core dump file will be named like/tmp/core.7, where the number corresponds to the number of a PHP-FPM worker failed.
Problem #2
Great! Now we have core dump and could debug it.
But why we have a problem #2?
The problem is in PHP binaries that have no debug symbols. It's absolutely normal to strip these symbols after compilation, as this saves a memory and improves performance. No one wants to have a fat and slow PHP binaries in production environment!
However, debugging without debug symbols is a total disaster.
Solution #2
Well, long story short - you need to recompile PHP and do not strip debug symbols. Here are the steps:
-
Checkout Git repository of Docker PHP images:
git clone https://github.com/docker-library/php cd php -
Go into directory where relevant
Dockerfileis located. If you are using PHP 8.2 under Alpine Linux, the path should be:
cd 8.2/alpine3.18/fpm -
Now open
Dockerfileand find following lines:
find \ /usr/local \ -type f \ -perm '/0111' \ -exec sh -euxc ' \ strip --strip-all "$@" || : \ ' -- '{}' + \ ; \ Completely delete these lines out of
Dockerfileand save it.-
Build the image and tag it:
docker build -t php-fpm-debug . Now you have an image with PHP compiled with debug symbols. What's next? Yes, build your app image from this one!
In
Dockerfileof your app replaceFROM ...withFROM php-fpm-debug:latest.Build it! But be aware to not use
--pullargument, so docker will search local repository for the specified image.
Result
Now, if segfault will happen you will be able to find core dump in /tmp directory and it will contain debug symbols for easier debugging process.
P.S. If you know a good article or tutorial on how to use GDB or Valgrind to debug PHP core dumps, please leave in a comment below. Much appreciated!
Top comments (0)