Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

"uninitialized value in subroutine exit" warning from CGI.pm

by dwmcewan (Initiate)
on Nov 10, 2017 at 02:53 UTC ( [id://1203097]=perlquestion: print w/replies, xml ) Need Help??

dwmcewan has asked for the wisdom of the Perl Monks concerning the following question:

Hi

I noticed some "uninitialized value in subroutine exit at /usr/pkg/lib/perl5/vendor_perl/5.26.0/CGI.pm line 472" warnings in our web server logs.

This was with CGI.pm version 4.36 running on "perl 5, version 26, subversion 0 (v5.26.0) built for x86_64-linux-thread-multi"

This warning was being generated by the following code in CGI.pm

sub self_or_default { return @_ if defined($_[0]) && (!ref($_[0])) &&($_[0] eq 'CGI'); unless (defined($_[0]) && (ref($_[0]) eq 'CGI' || UNIVERSAL::isa($_[0],'CGI')) # sli +ghtly optimized for common case ) { $Q = $CGI::DefaultClass->new unless defined($Q); unshift(@_,$Q); } return wantarray ? @_ : $Q; # <---- Line 472 of CGI.pm v4.36 }

I managed to track this down to some calls our code was making that were essentially:

print $cgi->textfield(-name=>"end_date", -default=>substr($pa->get_end_date(),0,10));

The $pa->get_end_date() call is part of a locally developed system that essentially queries a MySQL database. In the cases where the warning was appearing, the returned date was undefined (it was NULL in the DB)

Now, in retrospect I realise that this code was buggy and it has now been rewritten to handle undef dates. But what I don't understand, and was hoping to have explained here, is why the warning was coming out from somewhere deep within CGI.pm, rather than from the call to substr with an undefined first parameter

If I rewrite the above code to do the substr before the call to $cgi-textfield() (without also protecting it from an undef return value) I get the expected "Use of uninitialized value in substr at ..." warning.

I'd guess that its something to do with the differing contexts in which the substr is evaluated in, but a more definitive explanation would be greatly appreciated

Thanks,

Duncan

Replies are listed 'Best First'.
Re: "uninitialized value in subroutine exit" warning from CGI.pm
by haukex (Archbishop) on Nov 10, 2017 at 10:30 UTC

    Interesting, the only Google result for "uninitialized value in subroutine exit" (with quotes) is currently this thread. Here's an SSCCE to reproduce:

    #!perl -w use warnings; use strict; use CGI; print "$] $CGI::VERSION\n"; sub Foo::get_end_date { return undef } my $pa = bless {}, 'Foo'; print CGI->new->textfield(-name=>"end_date", -default=>substr($pa->get_end_date(),0,10)), "\n"; __END__ 5.026000 4.36 Use of uninitialized value in subroutine exit at .../CGI.pm line 472. <input type="text" name="end_date" />

    And a minimal test case is

    $ perl -wle 'sub x{@_} print x(substr undef,0)'

    On Perl 5.6 thru 5.14, this dies with "Use of uninitialized value in substr at -e line 1. Modification of a read-only value attempted at -e line 1." (which also doesn't seem quite right to me), whereas on Perls 5.16 thru 5.26 it gives the warning "Use of uninitialized value in subroutine exit at -e line 1.". A bisect shows the behavior changed with commit a74fb2cdc8f2. <update> I'm not an expert on the internals so I don't know if that commit is a red herring, or if the change was an unintended side effect of that commit, there is another commit that explains the change better, or something else. See replies. </update> Perl v5.16 was also the release of the "substr lvalue revamp", so I suspect a lot of code related to substr was modified. The release notes also say:

    Passing a substring of a read-only value or a typeglob to a function (potential lvalue context) no longer causes an immediate "Can't coerce" or "Modification of a read-only value" error. That error occurs only if the passed value is assigned to.

    Like LanX I suspect the whole thing has to do with the fact that substr is magical and returns a special lvalue that can modify the original string, in combination with the elements of @_ being aliases to the original parameters (Update before posting: as also said here by dave_the_m, who has deep knowledge of the internals). <update> I don't know what's going on at subroutine exit that would trigger the warning, but this issue may even be worth mentioning on P5P. See replies. </update> A few more comments on your code:

    • You've already said that you changed your code to avoid this in the first place; my suggestion would be to add a method to your API next to get_end_date (or a parameter to that method) that returns the 10-character string you want to display (I assume YYYY-MM-DD or something similar), perhaps even using "proper" methods for date/time handling like the core Time::Piece, the powerful DateTime, or functions provided by the database.

    • Perhaps you want to consider avoiding calling functions in the arguments to CGI methods in general, there have even been some vulnerabilities related to that, although I don't want to spread any FUD here - substr will probably never return anything other than a single value, and your code may very well not use the affected pattern anywhere, I'm just pointing this out as something to keep in mind.

    • Personally, I like to code defensively and sometimes will intentionally write ''.substr(...) to force the return value to be turned into a regular string if I suspect a risk of strange things happening with the lvalue.

    • I suspect you have -w on your shebang line. Note that the Perl documentation includes a section "What's wrong with * w* and $^W" (emphasis mine):

      Although very useful, the big problem with using -w on the command line to enable warnings is that it is all or nothing. Take the typical scenario when you are writing a Perl program. Parts of the code you will write yourself, but it's very likely that you will make use of pre-written Perl modules. If you use the -w flag in this case, you end up enabling warnings in pieces of code that you haven't written. ...

      I recommend you just use warnings; in your code and remove the -w from the shebang line, there may be other places where using -w (or $^W) may trigger warnings in code you don't have control over.

      I don't know what's going on at subroutine exit that would trigger the warning
      On subroutine return, any return values are copied. If you return a substr lvalue, this will trigger the uninit warning at this point. For example this warns:
      sub f { $_[0] } $a = f(substr(undef,0,1));
      This is behaviour I would expect, and I'm not seeing a bug.

      Dave.

        On subroutine return, any return values are copied. [emphasis mine]

        D'oh! Thank you for the enlightenment on a Friday afternoon ;-)

        Update: Yep:

        $ perl -wMstrict *a = \substr(undef,0); # alias via glob $b = $a; # copy $a = 'x'; # assignment __END__ Use of uninitialized value in scalar assignment at - line 2. Modification of a read-only value attempted at - line 3.

        And just like above, on Perls <5.16 that dies with "Modification of a read-only value attempted at - line 1." (instead of line 3). So I should probably call the commit I referenced a bugfix! :-)

        Additional updates: Expanded the code example from the original version and added (and edited) the last paragraph.

      on a side note, the warning doesn't appear inside the debugger

      C:\Windows\system32>perl -e "use warnings; sub x{@_} print x(substr un +def,0)" Use of uninitialized value in subroutine exit at -e line 1. C:\Windows\system32>perl -de0 Loading DB routines from perl5db.pl version 1.37 ... DB<1> use warnings; sub x{@_} print x(substr undef,0) DB<2> print $] 5.016003

      Cheers Rolf
      (addicted to the Perl Programming Language and ☆☆☆☆ :)
      Je suis Charlie!

Re: "uninitialized value in subroutine exit" warning from CGI.pm
by NetWallah (Canon) on Nov 10, 2017 at 03:36 UTC
    I was able to reproduce the problem you describe:
    perl -wE 'sub x{say qq|X Called|}; x substr(undef,0,4)'
    Basically, when substr(undef,..) is called as a parameter to a sub{},
    it behaves differently than when called without that context.

    I.E. it complains when called "solo", but suppresses the warning when called as a parameter.

    That's all I have.
    No idea on WHY the behaviour is different. I'm sure monks with more (perl) guts will come forth.

                    All power corrupts, but we need electricity.

      No idea on WHY the behaviour is different. I'm sure monks with more (perl) guts will come forth.
      Args to subs are treated as lvalues (sort of) because they can be modified within the sub, e.g.
      sub f { $_[0] = "abc" } $s = "1234"; f(substr($s, 0, 1)); # $s is now abc234

      Dave.

Re: "uninitialized value in subroutine exit" warning from CGI.pm
by LanX (Saint) on Nov 10, 2017 at 03:36 UTC
    Just a guess, I'm too tired ATM. ..

    Your substr code might return an empty list in list context, which would mean passing am odd number of arguments to textfield

    edit

    Another guess: substr can also return an lvalue which is passed by reference to the @_ aliases, thus causing a different (ie broken) error handling.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1203097]
Approved by holli
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2024-04-25 02:22 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found