Bug with cgi->header() + mod_perl ?

Bug with cgi->header() + mod_perl ?

am 11.02.2009 04:44:10 von Patrick Galbraith

Hello all,

I'm writing about the benefits of mod_perl in my book, having taken a
CGI script and using ModPerl::Registry turning it into a mod_perl app...
so on so forth... While testing what I'm writing about, I keep seeing
segfaults in my apache log. I started worrying that I have a bug in
DBD::mysql that I need to get fixing that I somehow missed in the fog of
writing a book the past 10 months, but found it had nothing to do with
that at all.

After looking at it in gdb, I noticed it has something to do with
Apache2::RequestRec, the header in particular.

I had a hunch, so I changed:

print $cgi->header('text/html');

To

print "Content-type: text/html\n\n";

Problem goes away. But a new problem manifests itself- the form
parameters aren't showing up properly

I use the line: $vars= $cgi->Vars();

And then access form parameters as $vars->{param}...

Sometimes they submit correctly, other times they don't. I have a couple
GET links and GET parameter parsing seems particularly erratic on
cgi->header() is taken out.

Is there something that I missed in the docs? Are there any particular
issues using CGI.pm with mod_perl that would be worth knowing about? I
will say from the debug output, CGI.pm seems to utilize
Apache2::RequestRec, which I was wondering about.


Also, this makes an interesting issue to include using $cgi->header() in
the book when it causes a segfault.

Note- this segfaulting occurs on two completely different boxes, same
fix applies.

Here's the output of the debugger:

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 47801312445536 (LWP 12196)]
0x00002b79a752c9f6 in XS_Apache2__RequestRec_send_cgi_header
(my_perl=0x1d5c82a0, cv=)
at Response.xs:149
149 mpxs_Apache2__RequestRec_send_cgi_header(r, buffer);


.....

(gdb) bt
#0 0x00002b79a752c9f6 in XS_Apache2__RequestRec_send_cgi_header
(my_perl=0x1d5c82a0,
cv=) at Response.xs:149
#1 0x0000003971890996 in Perl_pp_entersub ()
from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libper l.so
#2 0x000000397188a23e in Perl_runops_standard ()
from /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE/libper l.so
#3 0x00000039718375f0 in Perl_call_sv ()


(gdb) frame 0
#0 0x00002b79a752c9f6 in XS_Apache2__RequestRec_send_cgi_header
(my_perl=0x1d5c82a0,
cv=) at Response.xs:149
149 mpxs_Apache2__RequestRec_send_cgi_header(r, buffer);
(gdb) list
144 Apache2::RequestRec r
145 SV * buffer
146
147
148 CODE:
149 mpxs_Apache2__RequestRec_send_cgi_header(r, buffer);
150
151
152 MODULE = Apache2::Response
153 PROTOTYPES: disabled

Re: Bug with cgi->header() + mod_perl ?

am 13.02.2009 01:55:00 von Perrin Harkins

On Tue, Feb 10, 2009 at 10:44 PM, Patrick Galbraith wrote:
> After looking at it in gdb, I noticed it has something to do with
> Apache2::RequestRec, the header in particular.

My guess is that you're keeping an old CGI or $r object around between
requests, or that this is some kind of special scenario where CGI.pm's
reset_globals is not being called.

> I use the line: $vars= $cgi->Vars();
>
> And then access form parameters as $vars->{param}...
>
> Sometimes they submit correctly, other times they don't. I have a couple GET
> links and GET parameter parsing seems particularly erratic on cgi->header()
> is taken out.

That also fits with my guess. Want to show us how you initialize your
$cgi object?

- Perrin

Re: Bug with cgi->header() + mod_perl ?

am 14.02.2009 11:39:24 von Patrick Galbraith

Perrin,

Thanks - as soon as I read what you said, I looked at the code and saw I
instantiated a CGI object at the top of the script, package-scoped so I
wouldn't have to pass it between subroutines. I thought I would test
this intuition by instantiating in the method that needed it, and pass
it to the subroutines that need it. Which worked, no more segfault.

The arrangement was basically:

#!/usr/bin/perl

use strict;
use CGI qw(:common);

....
my $webapp= new WebApp();

my $cgi= new CGI;

.....

main();

....

sub main {
...
my $form= $cgi->Vars();

$user= $webapp->getForm();

someform($user, $form, $cgi);
}

sub someform {
my ($user, $form, $cgi= @_;

...
print $cgi->header('text/html') <-- segfault

...
}

It dawned on me as soon as I read the top sentence you wrote -- try
making $cgi lexical, and pass to someform() (arbitrary name). Problem solved


So now

#!/usr/bin/perl

....

use CGI qw(:standard);

my $webapp= new WebApp();

.....

main()

sub main {
my $cgi= new CGI;
my $form = $cgi->Vars;
my $user= $webapp->getUser();

someform($user, $form, $cgi);
}

sub someform {
...
print $cgi->header('text/html'); <-- no more segfaults
}

So, this makes a good case for something that worked fine for CGI
(because no persistence) but in mod perl caused a problem due to a
package-scope CGI vs. lexical, a change that would have to be made from
going from CGI to ModPerl::Registry.

Thanks!

Patrick


Perrin Harkins wrote:
> On Tue, Feb 10, 2009 at 10:44 PM, Patrick Galbraith wrote:
>
>> After looking at it in gdb, I noticed it has something to do with
>> Apache2::RequestRec, the header in particular.
>>
>
> My guess is that you're keeping an old CGI or $r object around between
> requests, or that this is some kind of special scenario where CGI.pm's
> reset_globals is not being called.
>
>
>> I use the line: $vars= $cgi->Vars();
>>
>> And then access form parameters as $vars->{param}...
>>
>> Sometimes they submit correctly, other times they don't. I have a couple GET
>> links and GET parameter parsing seems particularly erratic on cgi->header()
>> is taken out.
>>
>
> That also fits with my guess. Want to show us how you initialize your
> $cgi object?
>
> - Perrin
>

Re: Bug with cgi->header() + mod_perl ?

am 14.02.2009 11:50:00 von aw

Patrick Galbraith wrote:
[...]
Hi.

Less nice, less neat, less classic, terrible style, whatever you want,
but which also works :

# my nifty cgi-bin

use strict;
use warnings;

{
package MyOwn;

no strict;
$CGI = '';

}

sub sub1 {

$MyOwn::CGI = new CGI();

}

sub sub2 {

my $name = $MyOwn::CGI->param('name');

}

I am not quite sure why it works, and I would appreciate if some
mod_perl guru could explain it. But it seems to, for years now. Each
invocation of the cgi-bin seems to get its own private copy, without
mixups nor warnings.
But maybe it's not thread-safe and I'm just very lucky ?

Re: Bug with cgi->header() + mod_perl ?

am 15.02.2009 19:22:13 von Perrin Harkins

On Sat, Feb 14, 2009 at 5:50 AM, Andr=E9 Warnier wrote:
> Less nice, less neat, less classic, terrible style, whatever you want, bu=
t
> which also works :

Yeah, I wouldn't really recommend this. Passing your $cgi object to
subs that need it is the best solution.

> I am not quite sure why it works, and I would appreciate if some mod_perl
> guru could explain it.

You're storing the CGI object in a global, and as long as your sub1
gets called on every request to put a new one in there before anything
else tries to use it, it will work. If you ever try to use it before
sub1 gets called, it will segfault. Not a very safe approach.

- Perrin

Re: Bug with cgi->header() + mod_perl ?

am 15.02.2009 19:27:13 von aw

Sorry, this list does not automatically set the reply-to, and I always
forget..

Perrin Harkins wrote:
> On Sat, Feb 14, 2009 at 5:50 AM, André Warnier wrote:
>> Less nice, less neat, less classic, terrible style, whatever you want, but
>> which also works :
>
> Yeah, I wouldn't really recommend this. Passing your $cgi object to
> subs that need it is the best solution.
>
>> I am not quite sure why it works, and I would appreciate if some mod_perl
>> guru could explain it.
>
> You're storing the CGI object in a global, and as long as your sub1
> gets called on every request to put a new one in there before anything
> else tries to use it, it will work. If you ever try to use it before
> sub1 gets called, it will segfault. Not a very safe approach.
>
Ok, but that would only happen if the same code was run to process
another request while this one is going on.
Since to my knowledge each Apache child only processes one request at a
time, and each child has its own interpreter then, when exactly could
that happen ? In Apache MPM ? or are there other cases possible ?

Not being snappy, would really like to know.

Re: Bug with cgi->header() + mod_perl ?

am 15.02.2009 19:34:41 von Perrin Harkins

In the code you showed, nothing calls sub1, so I wasn't sure what you
were thinking would happen there. If your real code does call it
first on every request, then it should work.

There's no need to worry about concurrency. Even when using threads,
each interpreter is separate and nothing is shared unless you
explicitly make it shared.

- Perrin

On Sun, Feb 15, 2009 at 1:27 PM, Andr=E9 Warnier wrote:
> Sorry, this list does not automatically set the reply-to, and I always
> forget..
>
> Perrin Harkins wrote:
>>
>> On Sat, Feb 14, 2009 at 5:50 AM, Andr=E9 Warnier wrote:
>>>
>>> Less nice, less neat, less classic, terrible style, whatever you want,
>>> but
>>> which also works :
>>
>> Yeah, I wouldn't really recommend this. Passing your $cgi object to
>> subs that need it is the best solution.
>>
>>> I am not quite sure why it works, and I would appreciate if some mod_pe=
rl
>>> guru could explain it.
>>
>> You're storing the CGI object in a global, and as long as your sub1
>> gets called on every request to put a new one in there before anything
>> else tries to use it, it will work. If you ever try to use it before
>> sub1 gets called, it will segfault. Not a very safe approach.
>>
> Ok, but that would only happen if the same code was run to process
> another request while this one is going on.
> Since to my knowledge each Apache child only processes one request at a
> time, and each child has its own interpreter then, when exactly could
> that happen ? In Apache MPM ? or are there other cases possible ?
>
> Not being snappy, would really like to know.
>
>
>

Re: Bug with cgi->header() + mod_perl ?

am 20.02.2009 19:19:16 von Mark Hedges

On Sat, 14 Feb 2009, Patrick Galbraith wrote:
> Thanks - as soon as I read what you said, I looked at the
> code and saw I instantiated a CGI object at the top of the
> script, package-scoped so I wouldn't have to pass it
> between subroutines. I thought I would test this intuition
> by instantiating in the method that needed it, and pass it
> to the subroutines that need it. Which worked, no more
> segfault.
> ...
> So now
>
> #!/usr/bin/perl
> ...
> use CGI qw(:standard);
> my $webapp= new WebApp();
> ....
> main()
> sub main {
> my $cgi= new CGI;
> my $form = $cgi->Vars;
> my $user= $webapp->getUser();
> someform($user, $form, $cgi);
> }
> sub someform {
> ...
> print $cgi->header('text/html'); <-- no more segfaults
> }

Just my two cents...

package WebApp;
sub new { bless { cgi => CGI->new() }, shift }
sub someform {
my ($self) = @_;
my $cgi = $self->{cgi};
print $cgi->header('text/html');
# ...
}

package main;
my $webapp = WebApp->new();
$webapp->someform();

Then as you expand your application you don't have to
keep passing it around. You get what you wanted in the
first place by using objects and containing all the
per-request objects like CGI within the one per-request
WebApp object.

Mark