0

Apache segmentation fault

У нас на ресурсе постоянно возникает ошибка segfault — [notice] child pid 9606 exit signal Segmentation fault (11).

Ошибку удалось повторить в тестовой среде.

Для выяснения причины и решения, сначала мы сняти coredump в момент возникновения ошибки, для этого:

  • Включить генерацию coredump в системе
    # ulimit -с unlimited
  • Убедиться, что в Apache знает куда складывать coredump
    CoreDumpDirectory /tmp
  • Перезапустить Apache

Дождались ошибки — получили желанный coredump.

Затем мы перенесли его в тестовую среду, где и производили дальнейшие действия.
В тестовой среде была развернута точная копия нашего приложения.

Посмотреть что происходит в coredump поможет утилита gdb:
# gdb /usr/sbin/httpd /path/to/coredump

В выводе имеем такое:

#0  0x408dfe62 in zval_mark_grey (pz=) at /usr/src/debug/php-5.3.3/Zend/zend_gc.c:372
#1  0x408e08b5 in gc_mark_roots () at /usr/src/debug/php-5.3.3/Zend/zend_gc.c:433
#2  gc_collect_cycles () at /usr/src/debug/php-5.3.3/Zend/zend_gc.c:660
#3  0x408c1fc5 in zend_deactivate () at /usr/src/debug/php-5.3.3/Zend/zend.c:900
#4  0x40868dc8 in php_request_shutdown (dummy=0x0) at /usr/src/debug/php-5.3.3/main/main.c:1634
#5  0x409508e4 in php_apache_request_dtor (r=0x520417d0) at /usr/src/debug/php-5.3.3/sapi/apache2handler/sapi_apache2.c:509
#6  php_handler (r=0x520417d0) at /usr/src/debug/php-5.3.3/sapi/apache2handler/sapi_apache2.c:681
#7  0x400266a1 in ap_run_handler (r=0x520417d0) at /usr/src/debug/httpd-2.2.15/server/config.c:158
#8  0x4002a426 in ap_invoke_handler (r=0x520417d0) at /usr/src/debug/httpd-2.2.15/server/config.c:376
#9  0x400371a8 in ap_process_request (r=0x520417d0) at /usr/src/debug/httpd-2.2.15/modules/http/http_request.c:282
#10 0x40033c98 in ap_process_http_connection (c=0x410bc878) at /usr/src/debug/httpd-2.2.15/modules/http/http_core.c:190
#11 0x4002ef11 in ap_run_process_connection (c=0x410bc878) at /usr/src/debug/httpd-2.2.15/server/connection.c:43
#12 0x4003caea in child_main (child_num_arg=) at /usr/src/debug/httpd-2.2.15/server/mpm/prefork/prefork.c:667
#13 0x4003ce7e in make_child (s=, slot=0) at /usr/src/debug/httpd-2.2.15/server/mpm/prefork/prefork.c:763
#14 0x4003de53 in perform_idle_server_maintenance (_pconf=0x4052c0a8, plog=0x4055a160, s=0x4052dfa0)
    at /usr/src/debug/httpd-2.2.15/server/mpm/prefork/prefork.c:898
#15 ap_mpm_run (_pconf=0x4052c0a8, plog=0x4055a160, s=0x4052dfa0) at /usr/src/debug/httpd-2.2.15/server/mpm/prefork/prefork.c:1102
#16 0x40010ad2 in main (argc=1, argv=0xbfdb2804) at /usr/src/debug/httpd-2.2.15/server/main.c:760

Для получения более подробной информации нужно собрать PHP с ключом —debug, а так же почитать как разбирать такие проблемы с php

Похожая проблема встречалась на форуме битрикса, и там же подсказки как это лечить.

Подозрение, что ошибка в garbage collector php, на github есть решение — ему мы и последовали.

Задача:

  • получить исходники php
  • исправить исходники php
  • собрать php

У нас используется CentOS, поэтому будем пересобрали rpm пакет.

Для этого надо поставить утилиты
# yum install yum-utils rpmdevtools
Получить исходники пакета
# yumdownloader --source php
У меня yum downloader выдал:

No source RPM found for php-5.3.3-38.el6.x86_64
No source RPM found for php-5.3.3-40.el6_6.x86_64
Nothing to download

В этом случае можно получить исходники из репозитария:
# curl -O http://vault.centos.org/6.6/updates/Source/SPackages/php-5.3.3-40.el6_6.src.rpm
Устанавить src пакет:
# rpm -ivh php-5.3.3-40.el6_6.src.rpm
В результате появится дирекория rpmbuild.
Теперь нужно установить зависимости для сборки пакета.
# yum-builddep /home/имя_пользователя/rpmbuild/SPECS/php.spec
Это действие установит целую кучу необходимых dev пакетов.
Затем следует получить сам исходный код:
# rpmbuild -bp ~/rpmbuild/SPECS/php.spec
В результате будет разархивирован пакет и наложены все патчи, описанные в spec файле.
Далее сделать копию исходников, внести изменения, сделать diff:
# cp -R ~/rpmbuild/BUILD/php-5.3.3 ~/rpmbuild/BUILD/php-5.3.3-1
# vi ~/rpmbuild/BUILD/php-5.3.3-1/Zend/zend_variables.c
diff правильно надо делать из директории rpmbuild, иначе мне потом пришлось в .patch файле указывать относительный путь к файлам.
# cd ~/rpmbuild && diff -ru ~/rpmbuild/BUILD/php-5.3.3/Zend/zend_variables.c ~/rpmbuild/BUILD/php-5.3.3-1/Zend/zend_variables.c > ~/rpmbuild/SOURCES/~/rpmbuild/SOURCES/php-5.3.3-zend-my.patch

После этого нужно изменить ~/rpmbuild/SPECS/php.spec файл, указав в нем патч. Для этого нужно исправить секции:

  • PatchN — в конце перечисления патчей указать созданный патч (например, Patch256: php-5.3.3-zend-my.patch)
  • %patchN -p1 — в секцию %prep добавить созданный патч (например, %patch256 -p1 -b .zend-my)
  • %changelog — ну хоть это и чисто для себя, но правило хорошего тона

Потом можно собрать rpm пакет для установки:
# rpmbuild -ba ~/rpmbuild/SPECS/php.spec

Так, как в beckend стоит apache с mod_php, пробуем подставить apache исправленный libphp5.so.

Для этого сначала надо разобрать созданный rpm пакет:
rpm2cpio php.x86_64.rpm | cpio -idmv

i: Restore archive
d: Create leading directories where needed
m: Retain previous file modification times when creating files
v: Verbose i.e. display progress

затем остановить apache
# service httpd stop
подставить ему патченый модуль
# cp -f /path/to/patchmodule/libphp5.so /usr/lib64/httpd/modules/libphp5.so
запустить apache
# service httpd start

В тестовой среде проблема ушла.

При следующем обновлении php, скорее всего, придется снова применять патченый libphp5.so. Вряд ли этот патч к этому времени попадет в основную ветку.

Alexey Egorychev

FreeBSD and Linux sysadmin. Know many systems like mailsystems, DB, WWW stack. Automation with salt, ansible. Monitoring with nagios, zabbix.