Subject: | [bug] Acme::BadExample runs with this script :-) |
Hello,
I came upon Acme::BadExample while reading perl.perl6.language. Under
SUPPORT it says:
You're kidding right? I tell you what. If you can find some way to make
this module run, I shall happily stump up a $100 reward, payable in your
choice of American dollars, Australian dollars, or as a vertical metre of
beer (cartons).
Well, my program uses Acme::BadExample and runs more or less normally.
So how did I do it? I consider using source filters here cheating, as any
program can be "filtered" to "1;"; so I had to apply the dor patch to my
otherwise unmodified perl-5.8.6 to make it parse the // die statement.
I have all of strict, warnings, diagnostics, English, less, locale, POSIX
and utf8, so no problems there. I don't have Win32::ProcessTable or
Unix::Processtable, but those can be faked by creating the appropriate
%INC entries. Note that Acme::BadExample doesn't touch %INC, only %INV.
Another nice touch is that it uses strict 'vars' and declares @CACHE but
then proceeds to fill %CACHE. More weirdness: it calls the ordinary function
SUPER::new, not SUPER->new (which is a special call to the inherited new);
and it opens /root/.<random> for reading, but then attempts to write to it.
The infinite loop contains a function call, so thanks to Perl's dynamically
scoped "last" we're out of there again. The various die() and system() calls
can all be overridden; delete $CORE::{system} is a no-op, since the CORE::
prefix is more of a parser hack than a real package.
The only real problems in this module are
1) the use of the // operator, which requires a core patch, and
2) the false return value, which makes require fail (unless you overload
string literals :-)
Here's my script:
#!/usr/local/bin/perl
use overload (); # see below
# preload a few modules so we can override their import() methods
use strict;
use warnings ();
use diagnostics ();
# make it all happen at compile time
BEGIN {
# disable system() and die() for everyone
*CORE::GLOBAL::system = sub { print ":system(@_)\n"; };
*CORE::GLOBAL::die = sub { print ":die(@_)\n"; };
# for whatever reason, Acme::BadExample calls SUPER::new, not
# SUPER->new in its constructor. that means we can just define
# it here.
sub SUPER::new { last }
# the "last" above is used to abort the two loops in Acme::BadExample.
# one of them would be an infinite loop, but luckily it calls
# Acme::BadExample->new, which calls SUPER::new (see above).
# A::BE wants to load three modules: it.pm, Win32/ProcessTable.pm and
# Unix/ProcessTable.pm. make all of them succeed by adding an @INC hook.
push @INC, sub {
open my $fh, '<', \'1';
$fh
};
# note: predefining entries in %INC would work too, since although
# A::BE tries to guard against that, it misspells INC as INV :-)
# make no strict, use strict and use warnings no-ops.
*strict::unimport = sub {};
*strict::import = sub {};
*warnings::import = sub {};
# insert constant overloading into A::BE.
# needed to turn the '' at the end into a true value, grr.
*diagnostics::import = sub {
overload::constant 'q' => sub { $_[1] || 1 };
};
# create non-empty Acme::SuperHappyFunGeneralExample here to
# stop use base '...' from complaining.
package Acme::SuperHappyFunGeneralExample;
sub foo {}
# redirect all bare prints to $_
open my $fh, '>', \$_;
select $fh;
}
use Acme::BadExample;
BEGIN {
# restore printing to STDOUT
select STDOUT;
# print "discarding collected output:\n--------------\n$_--------------\n";
}
# success
print "i told u i was hardcore\n";
__END__
I haven't tried it, but changing the last line of Acme::BadExample from '';
to undef; should make my script fail (I hope that counts as patch :-).
HTH, Lukas
use overload (); # see below
# preload a few modules so we can override their import() methods
use strict;
use warnings ();
use diagnostics ();
# make it all happen at compile time
BEGIN {
# disable system() and die() for everyone
*CORE::GLOBAL::system = sub { print ":system(@_)\n"; };
*CORE::GLOBAL::die = sub { print ":die(@_)\n"; };
# for whatever reason, Acme::BadExample calls SUPER::new, not
# SUPER->new in its constructor. that means we can just define
# it here.
sub SUPER::new { last }
# the "last" above is used to abort the two loops in Acme::BadExample.
# one of them would be an infinite loop, but luckily it calls
# Acme::BadExample->new, which calls SUPER::new (see above).
# A::BE wants to load three modules: it.pm, Win32/ProcessTable.pm and
# Unix/ProcessTable.pm. make all of them succeed by adding an @INC hook.
push @INC, sub {
open my $fh, '<', \'1';
$fh
};
# note: predefining entries in %INC would work too, since although
# A::BE tries to guard against that, it misspells INC as INV :-)
# make no strict, use strict and use warnings no-ops.
*strict::unimport = sub {};
*strict::import = sub {};
*warnings::import = sub {};
# insert constant overloading into A::BE.
# needed to turn the '' at the end into a true value, grr.
*diagnostics::import = sub {
overload::constant 'q' => sub { $_[1] || 1 };
};
# create non-empty Acme::SuperHappyFunGeneralExample here to
# stop use base '...' from complaining.
package Acme::SuperHappyFunGeneralExample;
sub foo {}
# redirect all bare prints to $_
open my $fh, '>', \$_;
select $fh;
}
use Acme::BadExample;
BEGIN {
# restore printing to STDOUT
select STDOUT;
# print "discarding collected output:\n--------------\n$_--------------\n";
}
# success
print "i told u i was hardcore\n";