Skip Menu |

This queue is for tickets about the Sys-Syslog CPAN distribution.

Report information
The Basics
Id: 55215
Status: resolved
Priority: 0/
Queue: Sys-Syslog

People
Owner: Nobody in particular
Requestors: alex-berger [...] gmx.ch
Cc:
AdminCc:

Bug Information
Severity: Critical
Broken in: 0.27
Fixed in: 0.29



Subject: Sys::Syslog might call exit which triggers DESTROY
Perl Version: 5.8.8 Distribution name: Sys::Syslog Operatingsystem: HP-UX, Solaris, Linux, possibliy any POSIX system There is a serious problem (bug) in the subroutine '_syslog_send_console' of the perl module Sys::Syslog. The problem arises due to the usage of exit to terminate a forked child process. Calling exit will trigger calls to any DESTROY methods of objects that are still alive at the time of the exit call. This script can be used to reproduce this problems. Use this command to reproduce the problem: destructor.pl fork Use this commands to try possible solutions for the problem destructor.pl fork exec destructor.pl fork _exit In our special case, our appliaction opened a database connection (DBI + Oracle DBD) and then called the syslog subroutine from Sys::Syslog. After this call to syslog our appliaction tried to use the DIB connection which produced an error as the connection has been closed by the child process created in syslog (when it triggered the DESTROY method of the oracle connection instance). For better understanding here is the chronology of the problem: PARENT CHILD ACTION X open DBI connection X call syslog(...) which in turn calls _syslog_send_console X fork and waitpid X write log message to /dev/console X call exit(...) X exit triggers DESTROY for all alive objects (class instances) X oracle DBD DESTROY tells the database server that the connection must be closed, then it closes the client side of the connection (system call close(...)) while the database server closes the server side of the connection. X The parent process tries to used the DBI connection which results in an error as it has been closed on the server side. The parent's filedescriptor is still open and valid but the peer (database) has closed the connection. So in order to solve this problem it must be prevented that the child process triggers the calls to DESTROY when it terminates. In my opinion there are three possible solutions to the problem (bugfix): 1. Do not use fork at all in _syslog_send_console 2. Do not call exit(...) in _syslog_send_console use POSIX::_exit(...) instead. _exit(...) will not trigger any calls to DESTROY. 3. Do not call exit(...) in _syslog_send_console use exec instead, for example exec 'echo', this will prevent any calls to DESTROY. So my suggestion is to change the following subroutine from Sys::Syslog sub _syslog_send_console { my ($buf) = @_; chop($buf); # delete the NUL from the end # The console print is a method which could block # so we do it in a child process and always return success # to the caller. if (my $pid = fork) { if ($options{nowait}) { return 1; } else { if (waitpid($pid, 0) >= 0) { return ($? >> 8); } else { # it's possible that the caller has other # plans for SIGCHLD, so let's not interfere return 1; } } } else { if (open(CONS, ">/dev/console")) { my $ret = print CONS $buf . "\r"; # XXX: should this be \x0A ? exit $ret if defined $pid; close CONS; } exit if defined $pid; } } to this: sub _syslog_send_console { my ($buf) = @_; chop($buf); # delete the NUL from the end # The console print is a method which could block # so we do it in a child process and always return success # to the caller. if (my $pid = fork) { if ($options{nowait}) { return 1; } else { if (waitpid($pid, 0) >= 0) { return ($? >> 8); } else { # it's possible that the caller has other # plans for SIGCHLD, so let's not interfere return 1; } } } else { use POSIX; if (open(CONS, ">/dev/console")) { my $ret = print CONS $buf . "\r"; # XXX: should this be \x0A ? POSIX::_exit($ret) if defined $pid; close CONS; } POSIX::_exit(0) if defined $pid; } }
Subject: destructor.pl
#!/usr/bin/perl -w package XX; sub DESTROY { my $self = shift; printf "%s.DESTROY(pid=%s)\n", $self, $$; } package main; my $x = bless {}, 'XX'; if ( $ARGV[0] && $ARGV[0] eq 'fork' ) { my $pid = fork; if ( $pid ) { waitpid($pid, 0); } else { if ( $ARGV[1] && $ARGV[1] eq 'exec' ) { exec 'echo'; } elsif ( $ARGV[1] && $ARGV[1] eq '_exit' ) { use POSIX; POSIX::_exit(0); } exit(0); } } exit(0); __DATA__ There is a serious problem (bug) in the subroutine '_syslog_send_console' of the perl module Sys::Syslog. The problem arises due to the usage of exit to terminate a forked child process. Calling exit will trigger calls to any DESTROY methods of objects that are still alive at the time of the exit call. This script can be used to reproduce this problems. Use this command to reproduce the problem: destructor.pl fork Use this commands to try possible solutions for the problem destructor.pl fork exec destructor.pl fork _exit In our special case, our appliaction opened a database connection (DBI + Oracle DBD) and then called the syslog subroutine from Sys::Syslog. After this call to syslog our appliaction tried to use the DIB connection which produced an error as the connection has been closed by the child process created in syslog (when it triggered the DESTROY method of the oracle connection instance). For better understanding here is the chronology of the problem: PARENT CHILD ACTION X open DBI connection X call syslog(...) which in turn calls _syslog_send_console X fork and waitpid X write log message to /dev/console X call exit(...) X exit triggers DESTROY for all alive objects (class instances) X oracle DBD DESTROY tells the database server that the connection must be closed, then it closes the client side of the connection (system call close(...)) while the database server closes the server side of the connection. X The parent process tries to used the DBI connection which results in an error as it has been closed on the server side. The parent's filedescriptor is still open and valid but the peer (database) has closed the connection. So in order to solve this problem it must be prevented that the child process triggers the calls to DESTROY when it terminates. In my opinion there are three possible solutions to the problem (bugfix): 1. Do not use fork at all in _syslog_send_console 2. Do not call exit(...) in _syslog_send_console use POSIX::_exit(...) instead. _exit(...) will not trigger any calls to DESTROY. 3. Do not call exit(...) in _syslog_send_console use exec instead, for example exec 'echo', this will prevent any calls to DESTROY. So my suggestion is to change the following subroutine from Sys::Syslog sub _syslog_send_console { my ($buf) = @_; chop($buf); # delete the NUL from the end # The console print is a method which could block # so we do it in a child process and always return success # to the caller. if (my $pid = fork) { if ($options{nowait}) { return 1; } else { if (waitpid($pid, 0) >= 0) { return ($? >> 8); } else { # it's possible that the caller has other # plans for SIGCHLD, so let's not interfere return 1; } } } else { if (open(CONS, ">/dev/console")) { my $ret = print CONS $buf . "\r"; # XXX: should this be \x0A ? exit $ret if defined $pid; close CONS; } exit if defined $pid; } } to this: sub _syslog_send_console { my ($buf) = @_; chop($buf); # delete the NUL from the end # The console print is a method which could block # so we do it in a child process and always return success # to the caller. if (my $pid = fork) { if ($options{nowait}) { return 1; } else { if (waitpid($pid, 0) >= 0) { return ($? >> 8); } else { # it's possible that the caller has other # plans for SIGCHLD, so let's not interfere return 1; } } } else { use POSIX; if (open(CONS, ">/dev/console")) { my $ret = print CONS $buf . "\r"; # XXX: should this be \x0A ? POSIX::_exit($ret) if defined $pid; close CONS; } POSIX::_exit(0) if defined $pid; } }
Subject: Re: [rt.cpan.org #55215] Sys::Syslog might call exit which triggers DESTROY
Date: Mon, 18 Apr 2011 16:22:21 +0200
To: bug-Sys-Syslog [...] rt.cpan.org
From: Sébastien Aperghis-Tramoni <saper [...] cpan.org>
Hello, Apologies for the delay before addressing this problem. Alexander Berger wrote via RT: Show quoted text
> There is a serious problem (bug) in the subroutine > '_syslog_send_console' > of the perl module Sys::Syslog. The problem arises due to the usage of > exit to terminate a forked child process. Calling exit will trigger > calls to any DESTROY methods of objects that are still alive at the > time of the exit call. This script can be used to reproduce this > problems.
Indeed, you are right. the calls to exit() have been replaced with POSIX::_exit() in release 0.29, just uploaded on the CPAN, sponsored by the Perl QA Hackathon 2011 in Amsterdam. -- Sébastien Aperghis-Tramoni Close the world, txEn eht nepO.