Mailing List Archive

[mp2] crash when using EV::signal 'CHLD' and backtick operator at the same time
-------------8<---------- Start Bug Report ------------8<----------
1. Problem Description:

To reproduce the problem, start from Debian vanilla server (I used
testing (buster)).
Install apache2, libapache2-mod-perl2, libev-perl
(I also used development version of perl, mod_perl and EV with the
same result)
Create 2 perl files with following content:

handler1.pl:
#!/usr/bin/perl
my $pwd = `/bin/pwd`;
1;
handler2.pl:
#!/usr/bin/perl
use EV;
my $w = EV::signal 'CHLD', sub {
print "SIGCHLD received\n";
};
1;
Reference them in two PerlRequire directive, either
in /etc/apache2/apache2.conf or in a separate conf file
in /etc/apache2/conf-enabled:
# order is important
PerlRequire "/path/to/handler1.pl"
PerlRequire "/path/to/handler2.pl"

==> Apache will crash on startup.

I did an in-depth analysis of this bug. Here is what happens:

The backtick operator in handler1.pl internally calls waitpid, which
internally waits for the SIGCHLD signal.
EV::signal 'CHLD' installs a signal handler using sigaction (see
EV-4.22.0: libev/ev.c::ev_signal_start). There is no provision to remove
this signal handler. The handler is located in EV.so, loaded by the XS
loader.

apache2 goes through 3 stages on startup: pre-config, destroy-config,
create-config
In the pre-config stage, the 2 PerlRequire commands are executed and
the signal handler is installed at process level.
In the destroy-config stage, all modules are removed, including
mod_perl, which consists in removing all XS modules (see mod_perl:
src/modules/perl/modperl_interp.c::modperl_interp_destroy())
The removal of EV.so does not restore the signal handler as there is
no cleanup in EV.so.
At this point the signal handler is still registered at process level
but points to invalid memory!
In the create-config stage, the config is recreated, handler1.pl is
reloaded and the backtick operator triggers a SIGCHLD, which causes a
SIGEV because the signal handler is not in memory (the EV.so library
will only be loaded when the second PerlRequire is executed).

Conclusion:
Unloading the XS modules relies on the modules to cleanup the global
state properly but it's clearly not the case of the EV module.
I tested a fix that consists in skipping the unloading of the XS
modules. While it works for my case, it is not a proper solution either
as there are circumstances where it would cause other bugs.
Ideally the EV module should register a cleanup function to restore
the signal handlers on unload.
There are similar potential issues when an XS module register some op
checker in PL_check static array: unloading the XS module does not
remove the handler because Perl does not provide an API to do it.
Hopefully, in this case unloading mod_perl will normally causes
libperl to unload as well, which cleans the PL_check array.

Clearly, Perl is not designed to be unloaded-reloaded.


2. Used Components and their Configuration:

*** mod_perl version 2.000011

*** using /home/ben/src/mod_perl-2.0/lib/Apache2/BuildConfig.pm

*** Makefile.PL options:
MP_APR_LIB => aprext
MP_APXS => /usr/bin/apxs
MP_COMPAT_1X => 1
MP_GENERATE_XS => 1
MP_LIBNAME => mod_perl
MP_USE_DSO => 1


*** /usr/sbin/apache2 -V
Server version: Apache/2.4.34 (Debian)
Server built: 2018-07-27T19:37:37
Server's Module Magic Number: 20120211:79
Server loaded: APR 1.6.3, APR-UTIL 1.6.1
Compiled using: APR 1.6.3, APR-UTIL 1.6.1
Architecture: 64-bit
Server MPM: event
threaded: yes (fixed thread count)
forked: yes (variable process count)
Server compiled with....
-D APR_HAS_SENDFILE
-D APR_HAS_MMAP
-D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
-D APR_USE_SYSVSEM_SERIALIZE
-D APR_USE_PTHREAD_SERIALIZE
-D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
-D APR_HAS_OTHER_CHILD
-D AP_HAVE_RELIABLE_PIPED_LOGS
-D DYNAMIC_MODULE_LIMIT=256
-D HTTPD_ROOT="/etc/apache2"
-D SUEXEC_BIN="/usr/lib/apache2/suexec"
-D DEFAULT_PIDLOG="/var/run/apache2.pid"
-D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
-D DEFAULT_ERRORLOG="logs/error_log"
-D AP_TYPES_CONFIG_FILE="mime.types"
-D SERVER_CONFIG_FILE="apache2.conf"

*** /usr/bin/ldd /usr/sbin/apache2
linux-vdso.so.1 (0x00007ffdc0dfa000)
libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f9abcf5b000)
libaprutil-1.so.0 => /lib/x86_64-linux-gnu/libaprutil-1.so.0
(0x00007f9abcd2f000)
libapr-1.so.0 => /lib/x86_64-linux-gnu/libapr-1.so.0
(0x00007f9abccf6000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0
(0x00007f9abccd5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9abcb18000)
libuuid.so.1 => /lib/x86_64-linux-gnu/libuuid.so.1 (0x00007f9abcb0f000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f9abcb03000)
libcrypt.so.1 => /lib/x86_64-linux-gnu/libcrypt.so.1
(0x00007f9abcac9000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f9abcac4000)
libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1
(0x00007f9abc892000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9abd085000)


*** (apr|apu)-config linking info

-L/usr/lib/x86_64-linux-gnu -laprutil-1
-L/usr/lib/x86_64-linux-gnu -lapr-1



*** /usr/local/bin/perl -V
Summary of my perl5 (revision 5 version 29 subversion 2) configuration:
Commit id: 48ae8dc39c5996cfd74bb0fefa5026f48ef8445e
Platform:
osname=linux
osvers=4.17.0-1-amd64
archname=x86_64-linux-thread-multi
uname='linux testing 4.17.0-1-amd64 #1 smp debian 4.17.8-1
(2018-07-20) x86_64 gnulinux '
config_args='-es -Dusethreads -A ccflags=-fPIC'
hint=recommended
useposix=true
d_sigaction=define
useithreads=define
usemultiplicity=define
use64bitint=define
use64bitall=define
uselongdouble=undef
usemymalloc=n
default_inc_excludes_dot=define
bincompat5005=undef
Compiler:
cc='cc'
ccflags ='-D_REENTRANT -D_GNU_SOURCE -fPIC -fwrapv
-fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include
-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'
optimize='-O2'
cppflags='-D_REENTRANT -D_GNU_SOURCE -fPIC -fwrapv
-fno-strict-aliasing -pipe -fstack-protector-strong
-I/usr/local/include'
ccversion=''
gccversion='8.2.0'
gccosandvers=''
intsize=4
longsize=8
ptrsize=8
doublesize=8
byteorder=12345678
doublekind=3
d_longlong=define
longlongsize=8
d_longdbl=define
longdblsize=16
longdblkind=3
ivtype='long'
ivsize=8
nvtype='double'
nvsize=8
Off_t='off_t'
lseeksize=8
alignbytes=8
prototype=define
Linker and Libraries:
ld='cc'
ldflags =' -fstack-protector-strong -L/usr/local/lib'

libpth=/usr/local/lib /usr/lib/gcc/x86_64-linux-gnu/8/include-fixed /usr/include/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib/../lib /usr/lib/x86_64-linux-gnu /usr/lib/../lib /lib /lib64 /usr/lib64
libs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
libc=libc-2.27.so
so=so
useshrplib=true
libperl=libperl.so
gnulibc_version='2.27'
Dynamic Linking:
dlsrc=dl_dlopen.xs
dlext=so
d_dlsymun=undef
ccdlflags='-Wl,-E
-Wl,-rpath,/usr/local/lib/perl5/5.29.2/x86_64-linux-thread-multi/CORE'
cccdlflags='-fPIC'
lddlflags='-shared -O2 -L/usr/local/lib -fstack-protector-strong'


Characteristics of this binary (from libperl):
Compile-time options:
HAS_TIMES
MULTIPLICITY
PERLIO_LAYERS
PERL_COPY_ON_WRITE
PERL_DONT_CREATE_GVSV
PERL_IMPLICIT_CONTEXT
PERL_MALLOC_WRAP
PERL_OP_PARENT
PERL_PRESERVE_IVUV
USE_64_BIT_ALL
USE_64_BIT_INT
USE_ITHREADS
USE_LARGE_FILES
USE_LOCALE
USE_LOCALE_COLLATE
USE_LOCALE_CTYPE
USE_LOCALE_NUMERIC
USE_LOCALE_TIME
USE_PERLIO
USE_PERL_ATOF
USE_REENTRANT_API
Built under linux
Compiled at Aug 14 2018 23:09:13
%ENV:
PERL_LWP_USE_HTTP_10="1"
@INC:
/usr/local/lib/perl5/site_perl/5.29.2/x86_64-linux-thread-multi
/usr/local/lib/perl5/site_perl/5.29.2
/usr/local/lib/perl5/5.29.2/x86_64-linux-thread-multi
/usr/local/lib/perl5/5.29.2

*** Packages of interest status:

Apache2 : -
Apache2::Request : -
CGI : -
ExtUtils::MakeMaker: 7.34
LWP : -
mod_perl : -
mod_perl2 : 2.000011


3. This is the core dump trace: (if you get a core dump):


root@testing:/var/log/apache2# gdb /usr/sbin/apache2
GNU gdb (Debian 8.1-4) 8.1
(gdb) run -DONE_PROCESS -DNO_DETACH -k start
Starting program: /usr/sbin/apache2 -DONE_PROCESS -DNO_DETACH -k start
[Thread debugging using libthread_db enabled]
Using host libthread_db library
"/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff6fe3d90 in ?? ()
(gdb) bt
#0 0x00007ffff6fe3d90 in ?? ()
#1 <signal handler called>
#2 0x00007ffff7cde214 in __waitpid (pid=15194, stat_loc=0x7fffffffe2c4,
options=0) at ../sysdeps/unix/sysv/linux/waitpid.c:30
#3 0x00007ffff6d68025 in Perl_wait4pid ()
from /usr/local/lib/perl5/5.29.2/x86_64-linux-thread-multi/CORE/libperl.so
#4 0x00007ffff6d6892e in Perl_my_pclose ()
from /usr/local/lib/perl5/5.29.2/x86_64-linux-thread-multi/CORE/libperl.so
#5 0x00007ffff6dd5b06 in Perl_pp_backtick ()
from /usr/local/lib/perl5/5.29.2/x86_64-linux-thread-multi/CORE/libperl.so
#6 0x00007ffff6d84bf6 in Perl_runops_standard ()
from /usr/local/lib/perl5/5.29.2/x86_64-linux-thread-multi/CORE/libperl.so
#7 0x00007ffff6cf9726 in Perl_eval_sv ()
from /usr/local/lib/perl5/5.29.2/x86_64-linux-thread-multi/CORE/libperl.so
#8 0x00007ffff6cf9cf5 in Perl_require_pv ()
from /usr/local/lib/perl5/5.29.2/x86_64-linux-thread-multi/CORE/libperl.so
#9 0x00007ffff779ecce in modperl_require_file ()
from /usr/lib/apache2/modules/mod_perl.so
#10 0x00007ffff7797e4e in modperl_config_apply_PerlRequire ()
from /usr/lib/apache2/modules/mod_perl.so
#11 0x00007ffff77959ac in modperl_startup.localalias ()
from /usr/lib/apache2/modules/mod_perl.so
#12 0x00007ffff77957c3 in modperl_startup.localalias ()
from /usr/lib/apache2/modules/mod_perl.so
#13 0x00007ffff7795c26 in modperl_init ()
from /usr/lib/apache2/modules/mod_perl.so
#14 0x00007ffff7795d96 in modperl_hook_init ()
from /usr/lib/apache2/modules/mod_perl.so
#15 0x00005555555af2b3 in ap_run_open_logs ()
#16 0x000055555558aef2 in main ()



This report was generated by /usr/local/bin/mp2bug on Wed Aug 15
21:27:41 2018 GMT.

-------------8<---------- End Bug Report --------------8<----------