Skip Menu |

This queue is for tickets about the Net-SSH-Perl CPAN distribution.

Report information
The Basics
Id: 42051
Status: new
Priority: 0/
Queue: Net-SSH-Perl

People
Owner: Nobody in particular
Requestors: jamie [...] audible.transient.net
Cc:
AdminCc:

Bug Information
Severity: Important
Broken in: 1.33
Fixed in: (no value)



Subject: Net::SSH::Perl w/o Crypt::RSA is subject to infinite recursion w/perl 5.8 or earlier
Net::SSH::Perl is fond of using the eval "use ModuleWhichMayNotBePresent;" idiom, particularly when it comes to detecting if RSA support is available or not, but with perl 5.8 and earlier this idiom can have surprising and unintended consequences. For example, consider: [8]snapdragon<~/show/>ls . A .: A A.pm t.pl A: B.pm [9]snapdragon<~/show/>cat t.pl use A; eval { A->x; }; print "First attempt fails with: $@----\n" if ($@); A->x; [10]snapdragon<~/show/>cat A.pm package A; use strict; use warnings; sub x { eval "use A::B;"; die "eval use of A::B from A::x failed: $@" if ($@); print "eval use of A::B from A::x did not fail\n"; A::B->z; } sub z { print "well hello there stranger!\n"; } 1; [11]snapdragon<~/show/>cat A/B.pm package A::B; use strict; use warnings; use A; use base 'A'; use ClassThatDoesNotExist; sub z { print "we'll never get this far\n"; } 1; [12]snapdragon<~/show/>perl -v This is perl, v5.8.4 built for i386-linux-thread-multi Copyright 1987-2004, Larry Wall Perl may be copied only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5 source kit. Complete documentation for Perl, including FAQ lists, should be found on this system using `man perl' or `perldoc perl'. If you have access to the Internet, point your browser at http://www.perl.com/, the Perl Home Page. [13]snapdragon<~/show/>perl t.pl First attempt fails with: eval use of A::B from A::x failed: Can't locate ClassThatDoesNotExist.pm in @INC (@INC contains: /etc/perl /usr/local/lib/perl/5.8.4 /usr/local/share/perl/5.8.4 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.8 /usr/share/perl/5.8 /usr/local/lib/site_perl .) at A/B.pm line 8. BEGIN failed--compilation aborted at A/B.pm line 8. Compilation failed in require at (eval 1) line 1. BEGIN failed--compilation aborted at (eval 1) line 1. ---- eval use of A::B from A::x did not fail well hello there stranger! The second time A::x is called $@ is false, and because of the ordering of the use base 'A'; statement in B.pm, the z subroutine from A.pm is inherited, but never overridden. With perl 5.10, the situation has improved and things don't get as out of hand: [287]deadhour<~/show/>perl -v This is perl, v5.10.0 built for i486-linux-gnu-thread-multi Copyright 1987-2007, Larry Wall Perl may be copied only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5 source kit. Complete documentation for Perl, including FAQ lists, should be found on this system using "man perl" or "perldoc perl". If you have access to the Internet, point your browser at http://www.perl.org/, the Perl Home Page. [288]deadhour<~/show/>perl t.pl First attempt fails with: eval use of A::B from A::x failed: Can't locate ClassThatDoesNotExist.pm in @INC (@INC contains: /etc/perl /usr/local/lib/perl/5.10.0 /usr/local/share/perl/5.10.0 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.10 /usr/share/perl/5.10 /usr/local/lib/site_perl .) at A/B.pm line 8. BEGIN failed--compilation aborted at A/B.pm line 8. Compilation failed in require at (eval 1) line 1. BEGIN failed--compilation aborted at (eval 1) line 1. ---- eval use of A::B from A::x failed: Attempt to reload A/B.pm aborted. Compilation failed in require at (eval 3) line 1. BEGIN failed--compilation aborted at (eval 3) line 1. So, what, you ask, does this have to do with Net::SSH::Perl? Well, take a look at Net::SSH::Perl::Key, specifically at the definition of the read_private subroutine (which happens to be done in a BEGIN block using a method factory of sorts). Now poke around in that module and notice how many places the eval "use $class;" idiom is present. Finally, take a look at the Net::SSH::Perl::Key::RSA class paying special attention to the order of use statements. In the RSA class, use base qw( Net::SSH::Perl::Key ); is placed before use Crypt::RSA; and this has the unfortunate side-effect of letting the Net::SSH::Perl::Key::RSA inhert Net::SSH::Perl::Key's read_private implementation, although the intent is clearly that it should be overriden latter on...except if Crypt::RSA isn't installed, that never happens. This sets us up for the situation where Net::SSH::Perl::Key's read_private is called from Net::SSH::Perl::Key::RSA, which results in run-away recursion. For that to happen we have to twice attempt to eval "use Net::SSH::Perl::Key::RSA;" in a single session when Crypt::RSA hasn't been installed, which unfortuately is all too possible to do accidently. A user with a RSA identity configured (or both RSA and DSA identities available), connecting to a host which attempts a ssh-rsa key exchange will trigger just a situation. I don't really have a suggestion for a better run-time class detection idiom (except to say it'd probably work better with older perl versions if the idiom was forced to evaluate once at compile time and have the runtime logic refer to some global indicator, instead of expecting repeated evals to do something they're not going to do). At the very least, in the Net::SSH::Perl::Key::RSA class, it'd be a good idea to move the "use base qw( Net::SSH::Perl::Key );" statement until after the use Crypt::RSA stuff so that if Crypt::RSA isn't installed, the hunk of the Net::SSH::Perl::Key::RSA namespace that's left around doesn't have anything nasty lurking in it. This would defang the infinite recursion case and instead stick the user with a "Can't locate object method" error, which is easier on resource consumption.