(In the post below, 'STDIN', 'STDOUT', and 'STDERR' refer to Perl
filehandles, while 'stdin', 'stdout', and 'stderr' refer to Perl file
descriptors.)

During migration of a web application server from Perl 5.00503 to Perl
5.6, we discovered a bug in mod_perl 1.23 and above.  Under Perl 5.6,
mod_perl ties both the STDIN and STDOUT filehandles to its Apache object,
which now implements the &OPEN and &FILENO methods.  However, mod_perl
erroneously hardcodes the POSIX file descriptor stdout into both methods.

This means that &open() and &fileno() calls on STDIN will operate on the
stdout fd, rather than the stdin fd.  This is clearly broken.  This
behavior can be easily verified with this test:

    die 'Bug!' if fileno (STDIN) == fileno (STDOUT);

Web applications that wish to fork off child processes that take input
from their parents will not work on account of this bug.  Module code that
does this include IPC::Open2 [1], IPC::Open3, and GPG::Interface.

Part of this process involves setting the child process's stdin, stdout,
and stderr to file descriptors created earlier by the parent.  To do this,
the code generally does something like the following:

    open STDIN, '<&=' . my_fileno( $stdin->child_end() );
    ...
    open STDOUT, '>&=' . my_fileno( $stdout->child_end() );
    ...

This code ultimately translates into POSIX dup2()s.  Without mod_perl, we
should see the following system call trace.  (Assume that
$stdin->child_end() evaluates to 10, and $stdout->child_end() evaluates to
11.)

    dup2 (0, 10);
    ...
    dup2 (1, 11);
    ...

However, under mod_perl >= 1.23 and Perl 5.6, we see the following
instead:

    dup2 (1, 10);
    ...
    dup2 (1, 11);
    ...

This causes child process read()s from stdin to fail.  In the case of
GPG::Interface, this causes GnuPG to encrypt a 0-byte string, which causes
the software to appear functional when it is actually not working.

...

Fixing this bug will probably take some work.  Not being a mod_perl
expert, I'm not sure if there's any appropriate way to distinguish between
STDIN and STDOUT in Apache.xs.  Assuming that there is no good way to do
this, perhaps one solution would be to:
    1. split all the code other than &OPEN and &FILENO away from
src/modules/perl/Apache.xs into an include file,
    2. create two different "Apache" classes, ApacheSTDIN and ApacheSTDOUT
with the &OPEN and &FILENO code, using the appropriate file descriptor
names, and including the common file created in step #1,
    3. bifurcate src/modules/perl/mod_perl.c:perl_bless_request_rec()
into a function for STDIN and a function for STDOUT, and 
    4. modify src/modules/perl/perlio.c:perl_stdin2client() and
perl_stdout2client() to call the appropriate perl_bless_request_rec_*
function.  

Thoughts?

...

In the meantime, to fix existing Perl applications that rely on
open(STDIN, ... and fileno(STDIN) to work correctly, there are several
workarounds.  Perhaps the easiest is to place an 'untie *STDIN;' before
those statements.  When you're doing this in a forked child process, it's
unlikely that you'll need the original mod_perl-tied filehandles again
anyway.  Another potential solution is to:
        1. pull the STDIN_FILENO constant out of POSIX.pm, 
        2. use IO::Handle.pm's new_from_fd() method to create
a filehandle from that file descriptor,
        3. and to substitute this new filehandle for STDIN in the code.

Now that tied filehandles are becoming more popular, and notwithstanding
this mod_perl bug, it's probably a good thing for code that expects to
work with the underlying system file descriptors to follow this latter
approach -- not just with STDIN, but with all three standard file
descriptors.  (A word of warning to those who implement this for stderr:
POSIX.pm in Perl 5.6 has a typo that will prevent you from using
STDERR_FILENO.  You'll have to define it by hand.  So it goes.)

We took this latter approach in fixing our application that uses
GPG::Interface and it works fine.


Regards,

- Paul


[1] See Darren Stuart Embry <[EMAIL PROTECTED]>'s earlier, unanswered
post to [EMAIL PROTECTED], "bidirectional pipe problem --- process
doesn't see input"  
<http://marc.theaimsgroup.com/?l=apache-modperl&m=97226120208208&w=2>



Reply via email to