Oktober, 2022

Howto debug PHP segfault

Dienstag, Oktober 11th, 2022

Es gibt hier 2 Möglichkeiten segfaults mit gdb anzuschauen. Zum debuggen benötigt man erstmal gdb und die debugs-symbole des Endsprechenden Modules. In meinem aktiven Fall war das, das soap Modul:

dnf install -y gdb
debuginfo-install php74-php-soap

Für das debugen eines coredumps muss man in der /etc/opt/remi/php82/php-fpm.d/www.conf folgendes einstellen:

rlimit_core = 0

Reload nicht vergessen: systemctl reload php74-php-fpm.service

Dem Kernel muss ein Pfad angegeben werden, der auch von php-fpm geschrieben werden kann. Achtung, im cordump können auch sensible Informationen stehen. Daher sollte ein Pfad gewählt werden, der nicht vom Webserver ausgeliefert wird:

echo '/tmp/coredump-%e.%p' > /proc/sys/kernel/core_pattern

Dann kann man sich den coredump anschauen, in dem man das Programm und den Dump angibt:

gdb /opt/remi/php82/root/usr/sbin/php-fpm coredump-php-fpm.852720

Dan gibt es noch die Möglichkeit des live Debuggings. Dafür habe ich die Parameter in php-fpm angepasst, dass nur noch 1 Prozess gestartet wird. So habe ich die Möglichkeit die segfaults sofort mitzubekommen (ob dies ein gangbarer Weg im produktiv Umgebungen ist, müsst ihr selbst Endscheiden):

/etc/opt/remi/php82/php-fpm.d/www.conf

pm.max_children = 1
pm.start_servers = 1
pm.min_spare_servers = 1
pm.max_spare_servers = 1

Reload nicht vergessen: systemctl reload php74-php-fpm.service

Mit `ps` kann man sehen das jetzt nur noch 2 Prozesse vorhanden sind. Wir hängen uns an den chaild:

ps axfu 
...
root      171156  0.0  0.0 493916 11688 ?        Ss   10:46   0:00 php-fpm: master process (/etc/opt/remi/php82/php-fpm.conf)
apache    189306  0.0  0.1 497068 26096 ?        S    10:53   0:00  \_ php-fpm: pool www

Dann kann ich mich mit gdb an den chaild Prozess hängen:

gdb -p 189306
...
(gdb) continue 
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x00007f60ce1f1c75 in __strlen_avx2 () from /lib64/libc.so.6

Backtrace, gibt einen den Output des laufenden Prozesses:

(gdb) bt
#0  0x00007f60ce1f1c75 in __strlen_avx2 () from /lib64/libc.so.6
#1  0x00007f60c1cdeb6d in get_param (function=function@entry=0x7f60cc2720c0, 
    param_name=param_name@entry=0x18 , index=, response=response@entry=1)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/ext/soap/soap.c:3978
#2  0x00007f60c1cdfe54 in serialize_response_call2 (body=body@entry=0x557c1ae411d0, function=function@entry=0x7f60cc2720c0, 
    function_name=function_name@entry=0x7f60cc256078 "openSessionResponse", uri=uri@entry=0x7f60cc2660c0 "urn:soapService", 
    ret=ret@entry=0x7ffcc3881630, version=version@entry=1, main=1, node=0x0)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/ext/soap/soap.c:3292
#3  0x00007f60c1ce509b in serialize_response_call (function=0x7f60cc2720c0, function_name=0x7f60cc256078 "openSessionResponse", 
    uri=0x7f60cc2660c0 "urn:soapService", ret=0x7ffcc3881630, headers=0x0, version=1)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/ext/soap/soap.c:3660
#4  0x00007f60c1ced386 in zim_SoapServer_handle (execute_data=0x7f60cc213090, return_value=)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/ext/soap/soap.c:1484
#5  0x00007f60cbcbe4f5 in xdebug_execute_internal () from /opt/remi/php82/root/usr/lib64/php/modules/xdebug.so
#6  0x0000557c196a02c8 in ZEND_DO_FCALL_SPEC_RETVAL_UNUSED_HANDLER ()
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/Zend/zend_vm_execute.h:1844
#7  execute_ex (ex=0x18) at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/Zend/zend_vm_execute.h:56047
#8  0x00007f60cbcbda4c in xdebug_execute_ex () from /opt/remi/php82/root/usr/lib64/php/modules/xdebug.so
#9  0x0000557c196a1932 in zend_execute (op_array=0x7f60cc280000, return_value=0x0)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/Zend/zend_vm_execute.h:60379
#10 0x0000557c1962ed15 in zend_execute_scripts (type=type@entry=8, retval=retval@entry=0x0, file_count=file_count@entry=3)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/Zend/zend.c:1780
#11 0x0000557c195c849a in php_execute_script (primary_file=) at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/main/main.c:2537
#12 0x0000557c1946e662 in main (argc=, argv=)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/sapi/fpm/fpm/fpm_main.c:1891

Die jeweiligen stop’s bekommt man mit frame:

(gdb) frame 1
#1  0x00007f60c1cdeb6d in get_param (function=function@entry=0x7f60cc2720c0, 
    param_name=param_name@entry=0x18 , index=, response=response@entry=1)
    at /usr/src/debug/php82-php-8.2.0~rc3-16.el8.remi.x86_64/ext/soap/soap.c:3978
3978			if ((tmp = zend_hash_str_find_ptr(ht, param_name, strlen(param_name))) != NULL) {

Eine liste des Sourcecodes bekommt man mit list, Voraussetzung sind hier die installierten debug symbole:

(gdb) list
3973		if (ht == NULL) {
3974		  return NULL;
3975		}
3976	
3977		if (param_name != NULL) {
3978			if ((tmp = zend_hash_str_find_ptr(ht, param_name, strlen(param_name))) != NULL) {
3979				return tmp;
3980			} else {
3981				ZEND_HASH_FOREACH_PTR(ht, tmp) {
3982					if (tmp->paramName && strcmp(param_name, tmp->paramName) == 0) {

Die Ausgabe der im frame gespeicherten variablen:

(gdb) info locals 
tmp = 
ht = 0x7f60cc260428

(gdb) print param_name
$1 = 0x18