[Rt-commit] rt branch, 3.8/protect-fd0, created. rt-3.8.13rc1-1-g38093f9

Alex Vandiver alexmv at bestpractical.com
Fri May 25 19:00:16 EDT 2012

The branch, 3.8/protect-fd0 has been created
        at  38093f9fd10cbbd58f09c725b88d1c35bfa9cd51 (commit)

- Log -----------------------------------------------------------------
commit 38093f9fd10cbbd58f09c725b88d1c35bfa9cd51
Author: Alex Vandiver <alexmv at bestpractical.com>
Date:   Fri May 25 18:15:58 2012 -0400

    Protect both FD 0 and FD 1 on mod_perl to avoid sendmailpipe errors
    Under the 'perl-script' handler on mod_perl, STDIN and STDOUT are
    effectively moved out of the way before the body of the request is run,
    such that they always appear to be closed in the handler.
    Unfortunately, the way this was done on mod_perl <= 2.0.4 was to
    actually close them, while storing away the handle in a higher FD for
    later restoration.  This means that, after the completion of the first
    request, FD 0 and FD 1 were left available for later system calls to
    obtain, as STDIN and STDOUT crept into higher-numbered FDs.
    While we explicitly opened FD 1 during the first request to protect
    against this sort of issue, no such protection was given to FD 0.  This
    allowed it to be claimed, in the second request, by the socket to the
    browser.  This causes failures in 'sendmailpipe' communication, as we
    are unable to dup STDIN onto FD 0 prior to calling IPC::Open2 (see
    b7a5a53).  The symptoms of this in Apache's error log varied, but
      Undefined value assigned to typeglob at RT/Interface/Email.pm line 459
      Apache2::RequestIO::rflush: (103) Software caused connection abort
      (9)Bad file descriptor: core_output_filter: writing data to the network
    Though these errors were only observed under the prefork MPM, there is
    no known reason that they could not occur under the worker MPM as well.
    Resolve this by ensuring that we always claim both FD 0 and FD 1 during
    the first request.  In doing so, we replace the possibly-brittle call to
    open(), where we can only hope we get the FD we want, with
    IO::Handle->new_from_fd(), which allows us to specify the FD we want.
    mod_perl 2.0.5 resolved this issue by never actually closing STDIN and
    STOUT, but rather local'ing them to be undef; this ensures that no later
    system calls will obtain them accidentally.  Forcing them open using
    new_from_fd, as this commit does, does not hurt matters, however.

diff --git a/bin/webmux.pl.in b/bin/webmux.pl.in
index 7aae041..296f649 100755
--- a/bin/webmux.pl.in
+++ b/bin/webmux.pl.in
@@ -60,24 +60,25 @@ package RT::Mason;
 our ($Nobody, $SystemUser, $Handler, $r);
-my $protect_fd;
+my $protect_fds;
 sub handler {
     ($r) = @_;
-    if ( !$protect_fd && $ENV{'MOD_PERL'} && exists $ENV{'MOD_PERL_API_VERSION'}
-        && $ENV{'MOD_PERL_API_VERSION'} >= 2 && fileno(STDOUT) != 1
+    if ( !$protect_fds && $ENV{'MOD_PERL'} && exists $ENV{'MOD_PERL_API_VERSION'}
+        && $ENV{'MOD_PERL_API_VERSION'} >= 2
     ) {
-        # under mod_perl2, STDOUT gets closed and re-opened, however new STDOUT
-        # is not on FD #1. In this case next IO operation will occupy this FD
-        # and make all system() and open "|-" dangerouse, for example DBI
-        # can get this FD for DB connection and system() call will close
-        # by putting grabage into the socket
-        open( $protect_fd, '>', '/dev/null' )
-          or die "Couldn't open /dev/null: $!";
-        unless ( fileno($protect_fd) == 1 ) {
-            warn "We opened /dev/null to protect FD #1, but descriptor #1 is already occupied";
-        }
+        # under mod_perl2, STDIN and STDOUT get closed and re-opened,
+        # however they are not on FD 0 and 1.  In this case, the next
+        # socket that gets opened will occupy one of these FDs, and make
+        # all system() and open "|-" calls dangerous; for example, the
+        # DBI handle can get this FD, which later system() calls will
+        # close by putting garbage into the socket.
+        $protect_fds = [];
+        push @{$protect_fds}, IO::Handle->new_from_fd(0, "r")
+            if fileno(STDIN) != 0;
+        push @{$protect_fds}, IO::Handle->new_from_fd(1, "w")
+            if fileno(STDOUT) != 1;
     local $SIG{__WARN__};


More information about the Rt-commit mailing list