Description
Description
Perl supports "hooks" in the @INC
array. The original idea was to allow for source filters, but because they were exposed as sub/method calls to the user the API opens up a number of troublesome issues which unfortunately the code does not account for. We have had bugs related to this previously, but we only fixed them superficially, we never addressed the fundamental issues involved.
The main problem is that @INC
hooks can and do modify the @INC
array, and the behavior when they do so is pretty much accidental as opposed to intended, and can even result in accesses freed memory.
So the first issue is that code like this:
perl -le'BEGIN { push @INC, sub { warn "overwriting INC array in glob"; *INC=[sub{warn "C"}]; }, sub{ warn "in B" } }; eval "require Frobnitz" or die $@'
will access the original @INC
array after it is freed. This just happens to work out to just not do anything as it sees the freed array as having 0 elements and stops processing entirely. (It may be that the sub itself is also destroyed before it finishes executing, i havent checked) Arguably this code should never access freed memory and should output this:
$ ./perl -le'BEGIN { push @INC, sub { warn "overwriting INC array in glob"; *INC=[sub{warn "in C"}]; }, sub{ warn "in B" } }; eval "require Frobnitz" or die $@'
overwriting INC array in glob at -e line 1.
in C at -e line 1.
Can't locate Frobnitz.pm in @INC (you may need to install the Frobnitz module) (@INC contains: CODE(0x556edbddb900)) at (eval 1) line 1.
Another interesting case is this:
perl -le'my $counter; BEGIN { push @INC, sub { warn "unshifting INC"; unshift @INC, sub{warn "in shift ",++$counter}; }, sub{ warn "in B" } }; eval "require Frobnitz" or die $@' 2>&1 | head
which outputs:
perl -le'my $counter; BEGIN { push @INC, sub { warn "unshifting INC"; unshift @INC, sub{warn "in shift ",++$counter}; }, sub{ warn "in B" } }; eval "require Frobnitz" or die $@' 2>&1 | head
unshifting INC at -e line 1.
unshifting INC at -e line 1.
unshifting INC at -e line 1.
unshifting INC at -e line 1.
unshifting INC at -e line 1.
unshifting INC at -e line 1.
unshifting INC at -e line 1.
unshifting INC at -e line 1.
unshifting INC at -e line 1.
unshifting INC at -e line 1.
....
eg, it infinite loops because the array keeps growing and the callback keeps getting called over and over. IMO it would be saner for all if this output this:
$ ./perl -le'my $counter; BEGIN { push @INC, sub { warn "A unshifting INC"; unshift @INC, sub{warn "C in shift ",++$counter}; }, sub{ warn "in B" } }; eval "require Frobnitz" or die $@' 2>&1 | head
A unshifting INC at -e line 1.
C in shift 1 at -e line 1.
in B at -e line 1.
Can't locate Frobnitz.pm in @INC (you may need to install the Frobnitz module) (@INC contains: CODE(0x559a13e94638) /home/yorton/perl5/perlbrew/perls/latest_blead/lib/site_perl/5.37.7/x86_64-linux-thread-multi /home/yorton/perl5/perlbrew/perls/latest_blead/lib/site_perl/5.37.7 /home/yorton/perl5/perlbrew/perls/latest_blead/lib/5.37.7/x86_64-linux-thread-multi /home/yorton/perl5/perlbrew/perls/latest_blead/lib/5.37.7 CODE(0x559a13e85910) CODE(0x559a13e857c0)) at (eval 1) line 1.
or with the reverse order of the "B" and "C" lines.
Another bug is this:
norole:yorton@oncidium:blead:~/git_tree/perl$ perl -le'my $counter; BEGIN { push @INC, bless(sub { warn "A"; }), sub{ warn "B" } }; eval "require Frobnitz" or die $@' 2>&1 | head
Can't locate object method "INC" via package "main" at (eval 1) line 1.
why is it calling the INC method on an object that does not support it, and why doesnt the error message say "in @inc hook" or something to that effect? IMO this should output the following:
$ perl -le'my $counter; BEGIN { push @INC, sub { warn "A"; }, sub{ warn "B" } }; eval "require Frobnitz" or die $@' 2>&1 | head
A at -e line 1.
B at -e line 1.
Can't locate Frobnitz.pm in @INC (you may need to install the Frobnitz module) (@INC contains: /home/yorton/perl5/perlbrew/perls/perl-5.34.1/lib/site_perl/5.34.1/x86_64-linux /home/yorton/perl5/perlbrew/perls/perl-5.34.1/lib/site_perl/5.34.1 /home/yorton/perl5/perlbrew/perls/perl-5.34.1/lib/5.34.1/x86_64-linux /home/yorton/perl5/perlbrew/perls/perl-5.34.1/lib/5.34.1 CODE(0x5649a78a39d8) CODE(0x5649a78a3a68)) at (eval 1) line 1.
Another issue is that @INC
hooks and the errors that are generated from them are not "crazy proofed", in the sense that you can tie @INC
, and then have it return a list of places to look, but then when the error message is constructed show a totally different set of places, that might not even have been inspected. I am not showing an example, but the way the code is structured it is entirely possible. It is somewhat debatable if they should be crazy proofed, but personally i think they should be. A simple example without tie is this:
perl -le'my $counter; BEGIN { push @INC, sub { @INC=()}, }; require Frobnitz;' 2>&1 | head
Can't locate Frobnitz.pm in @INC (you may need to install the Frobnitz module) (@INC contains:) at -e line 1.
I would argue that the error message should show where we checked, not an empty list.
IMO the @INC
hooks should ensure that any given ref is looked at only once during traversal of the @INC
array, and that after an @INC
returns we should either a) restart processing from the beginning, ignoring anything we have already seen, or b) continue and wrap around to do a full traversal back to the most recently executed @inc hook, assuming that index is still reachable. the former is easier to implement, but the latter might be more efficient. We also need to assume that the old @INC
array no longer exists after any @INC
hook callback.
Hopefully it is possibly to clean up these issues without imposing any noticeable costs on people doing lots of require statements.
Steps to Reproduce
Repro steps are above.
Expected behavior
@INC
hooks should never access freed memory.@INC
hooks should never fire more than once per require statement.- Modifications to
@INC
by hooks should be visible regardless as to where they are in the array. - Error message should show where we checked, and maybe info about the callbacks used, not the state of
@INC
at the end of the require. - Blessed subs with no INC methods should be called as a subroutine, not as an object which would produce a method call error.
Perl configuration
Not relevant. This ticket is correct to the best of knowledge at 5.37.7