Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Manipulating hash in SIGCHLD handler causes "Segmentation fault" #7251

Open
p5pRT opened this issue Apr 21, 2004 · 6 comments
Open

Manipulating hash in SIGCHLD handler causes "Segmentation fault" #7251

p5pRT opened this issue Apr 21, 2004 · 6 comments

Comments

@p5pRT
Copy link

p5pRT commented Apr 21, 2004

Migrated from rt.perl.org#29012 (status was 'open')

Searchable as RT29012$

@p5pRT
Copy link
Author

p5pRT commented Apr 21, 2004

From morimoto@interlink.ad.jp

Created by morimoto@interlink.ad.jp

Manipulating hash in signal handler (SIGCHLD) causes "Segmentation fault".
Run following script some times. "Segmentation fault" may happen at "POINT1"
or "POINT2".

$speed = 0.001 ; # more fast more error

$SIG{CHLD} = sub {
  while ( (my $pid = waitpid( -1, WNOHANG )) > 0 ) {
  if ( exists $children{$pid} ) {
  delete $children{$pid} ;
  } else {
  print "no such pid in the hash! ($pid)\n" ;
  }
  }
} ;

for ( my $i = 0 ; $i < 1000 ; $i++ ) {
  my $pid = fork ;
  if ( !defined $pid ) {
  print "fork : error\n" ;
  } elsif ( $pid ) {
  $children{$pid} = time ;
  } else {
  my $delay = rand( $speed ) ;
  select undef, undef, undef, $delay ;
  exit 0 ;
  }
  while ( keys %children > 20 ) { ### SEGV here??
  sleep 1 ;
  }
}

1 while wait > 0 ;
foreach my $pid ( keys %children ) {
  print "still living?? ($pid)\n" ;
}

Perl Info


This perlbug was built using Perl 5.00503 - Sun Mar  5 13:39:27 SAST 2000
It is being executed now by  Perl 5.008003 - Tue Apr  6 15:46:58 JST 2004.

Site configuration information for perl 5.008003:

Configured by yneko at Tue Apr  6 15:46:58 JST 2004.

Summary of my perl5 (revision 5.0 version 8 subversion 3) configuration:
  Platform:
    osname=freebsd, osvers=4.9-release, archname=i386-freebsd
    uname='freebsd lime.interlink.or.jp 4.9-release freebsd 4.9-release #0:
mon oct 27 17:51:09 gmt 2003
root@freebsd-stable.sentex.ca:usrobjusrsrcsysgeneric i386 '
    config_args='-de'
    hint=recommended, useposix=true, d_sigaction=define
    usethreads=undef use5005threads=undef useithreads=undef
usemultiplicity=undef
    useperlio=define d_sfio=undef uselargefiles=define usesocks=undef
    use64bitint=undef use64bitall=undef uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='cc', ccflags
='-DHAS_FPSETMASK -DHAS_FLOATINGPOINT_H -fno-strict-aliasing',
    optimize='-O',
    cppflags='-DHAS_FPSETMASK -DHAS_FLOATINGPOINT_H -fno-strict-aliasing'
    ccversion='', gccversion='2.95.4 20020320 [FreeBSD]', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t',
lseeksize=8
    alignbytes=4, prototype=define
  Linker and Libraries:
    ld='cc', ldflags ='-Wl,-E '
    libpth=/usr/lib
    libs=-lm -lcrypt -lutil -lc
    perllibs=-lm -lcrypt -lutil -lc
    libc=, so=so, useshrplib=false, libperl=libperl.a
    gnulibc_version=''
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags=' '
    cccdlflags='-DPIC -fPIC', lddlflags='-shared '

Locally applied patches:



@INC for perl 5.008003:
    /usr/local/lib/perl5/5.8.3/i386-freebsd
    /usr/local/lib/perl5/5.8.3
    /usr/local/lib/perl5/site_perl/5.8.3/i386-freebsd
    /usr/local/lib/perl5/site_perl/5.8.3
    /usr/local/lib/perl5/site_perl
    .


Environment for perl 5.008003:
    HOME=/root
    LANG (unset)
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin
:/usr/X11R6/bin:/root/bin
    PERL_BADLANG (unset)
    SHELL=/bin/csh



@p5pRT
Copy link
Author

p5pRT commented May 3, 2004

From @iabyn

On Wed, Apr 21, 2004 at 01​:25​:17AM -0000, Mao Morimoto wrote​:

Manipulating hash in signal handler (SIGCHLD) causes "Segmentation fault".
Run following script some times. "Segmentation fault" may happen at "POINT1"
or "POINT2".

$speed = 0.001 ; # more fast more error

$SIG{CHLD} = sub {
while ( (my $pid = waitpid( -1, WNOHANG )) > 0 ) {
if ( exists $children{$pid} ) {
delete $children{$pid} ;
} else {
print "no such pid in the hash! ($pid)\n" ;
}
}
} ;

for ( my $i = 0 ; $i < 1000 ; $i++ ) {
my $pid = fork ;
if ( !defined $pid ) {
print "fork : error\n" ;
} elsif ( $pid ) {
$children{$pid} = time ;
} else {
my $delay = rand( $speed ) ;
select undef, undef, undef, $delay ;
exit 0 ;
}
while ( keys %children > 20 ) { ### SEGV here??
sleep 1 ;
}
}

1 while wait > 0 ;
foreach my $pid ( keys %children ) {
print "still living?? ($pid)\n" ;
}

Thanks for the report. The problem boils down to the following​:
The code
  $children{$pid} = time ;

gets interrupted by the signal after $children{$pid} has been pushed on
the stack (helem has executed), but before the assignment. Then in the
signal handler, this value is freed by the delete. On return from the
signal handler, the assignment is done to a freed value, and badness
follows.

Yet another side-effect of stuff on the stack not being ref-counted.
I can't see a way of making this safe.

--
"You're so sadly neglected, and often ignored.
A poor second to Belgium, When going abroad."
  -- Monty Python - "Finland"

@p5pRT
Copy link
Author

p5pRT commented May 3, 2004

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented May 5, 2004

From perl5-porters@ton.iguana.be

In article <20040503232914.GL1895@​iabyn.com>,
  Dave Mitchell <davem@​iabyn.com> writes​:

On Wed, Apr 21, 2004 at 01​:25​:17AM -0000, Mao Morimoto wrote​:

Manipulating hash in signal handler (SIGCHLD) causes "Segmentation fault".
Thanks for the report. The problem boils down to the following​:
The code
$children{$pid} = time ;

gets interrupted by the signal after $children{$pid} has been pushed on
the stack (helem has executed), but before the assignment. Then in the
signal handler, this value is freed by the delete. On return from the
signal handler, the assignment is done to a freed value, and badness
follows.

Yet another side-effect of stuff on the stack not being ref-counted.
I can't see a way of making this safe.

Since this is a case of "don't do that then", you can however
work around this by detecting your child exits synchronously. This
you can often do by leaving an open write side of a pipe in each of
your children (one pipe per child) (see $^F if your children use exec()).
Then in the parent use EOF on the read side as an indication of process
death. And these you can multiplex on using poll/select. Since quite
often you leave a communication channel to your children anyways,
you probably can use that instead of an extra pipe.

@p5pRT
Copy link
Author

p5pRT commented May 5, 2004

From yneko@interlink.ad.jp

Thank you for your advice.
I could correct the problem by processing child exits without using the
signals.
But I hope, please make signals more safe. It is really useful mechanism.

thank you!
- Mao Morimoto

----- Original Message -----
From​: "(Ton Hospel) via RT" <perlbug-followup@​perl.org>
To​: <morimoto@​interlink.ad.jp>
Sent​: Wednesday, May 05, 2004 10​:32 PM
Subject​: Re​: [perl #29012] Manipulating hash in SIGCHLD handler causes
"Segmentation fault"

In article <20040503232914.GL1895@​iabyn.com>,
Dave Mitchell <davem@​iabyn.com> writes​:

On Wed, Apr 21, 2004 at 01​:25​:17AM -0000, Mao Morimoto wrote​:

Manipulating hash in signal handler (SIGCHLD) causes "Segmentation
fault".
Thanks for the report. The problem boils down to the following​:
The code
$children{$pid} = time ;

gets interrupted by the signal after $children{$pid} has been pushed on
the stack (helem has executed), but before the assignment. Then in the
signal handler, this value is freed by the delete. On return from the
signal handler, the assignment is done to a freed value, and badness
follows.

Yet another side-effect of stuff on the stack not being ref-counted.
I can't see a way of making this safe.

Since this is a case of "don't do that then", you can however
work around this by detecting your child exits synchronously. This
you can often do by leaving an open write side of a pipe in each of
your children (one pipe per child) (see $^F if your children use exec()).
Then in the parent use EOF on the read side as an indication of process
death. And these you can multiplex on using poll/select. Since quite
often you leave a communication channel to your children anyways,
you probably can use that instead of an extra pipe.

@p5pRT
Copy link
Author

p5pRT commented May 5, 2004

From @iabyn

On Wed, May 05, 2004 at 11​:03​:41PM +0900, Mao Morimoto wrote​:

Thank you for your advice.
I could correct the problem by processing child exits without using the
signals.
But I hope, please make signals more safe. It is really useful mechanism.

Well, the main difficulty here is that if the child exits quickly,
the signal handler is trying to delete the entry in the hash before the
main process has finished inserting that entry into the hash! It is the
fault of perl that this causes a crash (and is hard to fix), but even
if we fixed it, your code would still be logically wrong. What you could
do, both to make your code 'correct' and to avoid triggering a perl crash,
is delay the delivery of signals while the pid is being added to the
hash. This can be done via the POSIX module (see page 418 of "Programming
Perl" 3rd edition for more info).

Here's a modified version of your code which does jjust that.
Dave.

$speed = 0.001 ; # more fast more error

$SIG{CHLD} = sub {
  while ( (my $pid = waitpid( -1, WNOHANG )) > 0 ) {
  if ( exists $children{$pid} ) {
  delete $children{$pid} ;
  } else {
  print "no such pid in the hash! ($pid)\n" ;
  }
  }
} ;

{
  use POSIX qw(​:signal_h);
  my $sigset = POSIX​::SigSet->new;
  my $blockset = POSIX​::SigSet->new(SIGCHLD);
  sub block { sigprocmask(SIG_BLOCK, $blockset, $sigset) }
  sub unblock { sigprocmask(SIG_SETMASK, $sigset) }
}

for ( my $i = 0 ; $i < 1000 ; $i++ ) {
  block;
  my $pid = fork ;
  if ( !defined $pid ) {
  unblock;
  print "fork : error\n" ;
  } elsif ( $pid ) {
  $children{$pid} = time ;
  unblock;
  } else {
  unblock;
  my $delay = rand( $speed ) ;
  select undef, undef, undef, $delay ;
  exit 0 ;
  }
  while ( keys %children > 20 ) { ### SEGV here??
  sleep 1 ;
  }
}

1 while wait > 0 ;
foreach my $pid ( keys %children ) {
  print "still living?? ($pid)\n" ;
}

--
"There's something wrong with our bloody ships today, Chatfield."
  -- Admiral Beatty at the Battle of Jutland, 31st May 1916.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants