File Coverage

blib/lib/Lexical/SealRequireHints.pm
Criterion Covered Total %
statement 26 27 96.3
branch 1 2 50.0
condition n/a
subroutine 9 10 90.0
pod n/a
total 36 39 92.3


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             Lexical::SealRequireHints - prevent leakage of lexical hints
4              
5             =head1 SYNOPSIS
6              
7             use Lexical::SealRequireHints;
8              
9             =head1 DESCRIPTION
10              
11             This module works around two historical bugs in Perl's handling of the
12             C<%^H> (lexical hints) variable. One bug causes lexical state in one
13             file to leak into another that is Cd/Cd/Ced from it.
14             This bug, [perl #68590], was present from Perl 5.6 up to Perl 5.10, fixed
15             in Perl 5.11.0. The second bug causes lexical state (normally a blank
16             C<%^H> once the first bug is fixed) to leak outwards from C, if
17             it is automatically loaded during Unicode regular expression matching,
18             into whatever source is compiling at the time of the regexp match.
19             This bug, [perl #73174], was present from Perl 5.8.7 up to Perl 5.11.5,
20             fixed in Perl 5.12.0.
21              
22             Both of these bugs seriously damage the usability of any module relying
23             on C<%^H> for lexical scoping, on the affected Perl versions. It is in
24             practice essential to work around these bugs when using such modules.
25             On versions of Perl that require such a workaround, this module globally
26             changes the behaviour of C, including C and the implicit
27             C performed in Unicode regular expression matching, and of C,
28             so that they no longer exhibit these bugs.
29              
30             The workaround supplied by this module takes effect the first time its
31             C method is called. Typically this will be done by means of a
32             C statement. This should be done as early as possible, because it
33             only affects C/C/C statements that are compiled after
34             the workaround goes into effect. For C statements, and C
35             and C statements that are executed immediately and only once,
36             it suffices to invoke the workaround when loading the first module
37             that will set up vulnerable lexical state. Delayed-action C
38             and C statements, however, are more troublesome, and can require
39             the workaround to be loaded much earlier. Ultimately, an affected Perl
40             program may need to load the workaround as very nearly its first action.
41             Invoking this module multiple times, from multiple modules, is not a
42             problem: the workaround is only applied once, and applies to everything
43             subsequently compiled.
44              
45             This module is implemented in XS, with a pure Perl backup version for
46             systems that can't handle XS modules. The XS version has a better
47             chance of playing nicely with other modules that modify C
48             or C handling. The pure Perl version can't work at all on some
49             Perl versions; users of those versions must use the XS. On all Perl
50             versions suffering the underlying hint leakage bug, pure Perl hooking
51             of C breaks the use of C without an explicit parameter
52             (implicitly using C<$_>).
53              
54             =head1 PERL VERSION DIFFERENCES
55              
56             The history of the C<%^H> bugs is complex. Here is a chronological
57             statement of the relevant changes.
58              
59             =over
60              
61             =item Perl 5.6.0
62              
63             C<%^H> introduced. It exists only as a hash at compile time. It is not
64             localised by C/C, so lexical hints leak into every module
65             loaded, which is bug [perl #68590].
66              
67             The C mechanism doesn't work cleanly for C, because
68             overriding C loses the necessary special parsing of bareword
69             arguments to it. As a result, pure Perl code can't properly globally
70             affect the behaviour of C. Pure Perl code can localise C<%^H>
71             itself for any particular C invocation, but a global fix is
72             only possible through XS.
73              
74             =item Perl 5.7.2
75              
76             The C mechanism now works cleanly for C, so pure
77             Perl code can globally affect the behaviour of C to achieve a
78             global fix for the bug.
79              
80             =item Perl 5.8.7
81              
82             When C is automatically loaded during Unicode regular expression
83             matching, C<%^H> now leaks outward from it into whatever source is
84             compiling at the time of the regexp match, which is bug [perl #73174].
85             It often goes unnoticed, because [perl #68590] makes C<%^H> leak into
86             C which then doesn't modify it, so what leaks out tends to
87             be identical to what leaked in. If [perl #68590] is worked around,
88             however, C<%^H> tends to be (correctly) blank inside C, and
89             this bug therefore blanks it for the outer module.
90              
91             =item Perl 5.9.4
92              
93             C<%^H> now exists in two forms. In addition to the relatively ordinary
94             hash that is modified during compilation, the value that it had at each
95             point in compilation is recorded in the compiled op tree, for later
96             examination at runtime. It is in a special representation-sharing
97             format, and writes to C<%^H> are meant to be performed on both forms.
98             C/C does not localise the runtime form of C<%^H> (and still
99             doesn't localise the compile-time form).
100              
101             A couple of special C<%^H> entries are erroneously written only to the
102             runtime form.
103              
104             Pure Perl code, although it can localise the compile-time C<%^H> by
105             normal means, can't adequately localise the runtime C<%^H>, except by
106             using a string eval stack frame. This makes a satisfactory global fix
107             for the leakage bug impossible in pure Perl.
108              
109             =item Perl 5.10.1
110              
111             C/C now properly localise the runtime form of C<%^H>,
112             but still not the compile-time form.
113              
114             A global fix is once again possible in pure Perl, because the fix only
115             needs to localise the compile-time form.
116              
117             =item Perl 5.11.0
118              
119             C/C now properly localise both forms of C<%^H>, fixing
120             [perl #68590]. This makes [perl #73174] apparent without any workaround
121             for [perl #68590].
122              
123             The special C<%^H> entries are now correctly written to both forms of
124             the hash.
125              
126             =item Perl 5.12.0
127              
128             The automatic loading of C during Unicode regular expression
129             matching now properly restores C<%^H>, fixing [perl #73174].
130              
131             =back
132              
133             =cut
134              
135             package Lexical::SealRequireHints;
136              
137 27     27   1585242 { use 5.006; }
  27         221  
138 27     27   139 use warnings;
  27         56  
  27         1147  
139 27     27   130 use strict;
  27         60  
  27         2703  
140              
141             our $VERSION = "0.012";
142              
143             my($install_compilation_workaround, $install_full_workaround_idempotently);
144             $install_full_workaround_idempotently = sub {
145             $install_full_workaround_idempotently =
146             sub { die "unsuccessful workaround installation" };
147             my $icw = $install_compilation_workaround;
148             $install_compilation_workaround = undef;
149             $icw->();
150             if(exists $INC{"AutoLoader.pm"}) {
151             # The "require" statements in AutoLoader were compiled
152             # before we put the workaround in place, and so are
153             # vulnerable. They're capable of loading an open-ended
154             # set of files, so the vulnerability can't be allowed
155             # to stand. So we delete AutoLoader's compiled code
156             # and load in anew, to get it compiled in a form that's
157             # subject to the workaround.
158 27     27   150 no strict "refs";
  27         56  
  27         4007  
159             my $dynaloader_shares = defined(&{"DynaLoader::AUTOLOAD"}) &&
160             \&{"DynaLoader::AUTOLOAD"} ==
161             \&{"AutoLoader::AUTOLOAD"};
162             foreach my $k (sort keys %{"AutoLoader::"}) {
163             undef *{"AutoLoader::$k"} unless $k =~ /::\z/;
164             }
165             delete $INC{"AutoLoader.pm"};
166             scalar(require AutoLoader);
167             if($dynaloader_shares) {
168 27     27   168 no warnings "redefine";
  27         65  
  27         1874  
169             *{"DynaLoader::AUTOLOAD"} = \&{"AutoLoader::AUTOLOAD"};
170             }
171             }
172             if(exists $INC{"utf8_heavy.pl"}) {
173             # The "require" and "do" statements in utf8_heavy.pl
174             # were compiled before we put the workaround in place,
175             # and so are vulnerable. They're capable of loading an
176             # open-ended set of files, so the vulnerability can't
177             # be allowed to stand. So we delete utf8_heavy.pl's
178             # compiled code and load in anew, to get it compiled in
179             # a form that's subject to the workaround.
180 27     27   147 no strict "refs";
  27         56  
  27         14801  
181             foreach(qw(DEBUG SWASHGET SWASHNEW croak DESTROY)) {
182             undef *{"utf8::$_"} if exists ${"utf8::"}{$_};
183             }
184             delete $INC{"utf8_heavy.pl"};
185             scalar(require "utf8_heavy.pl");
186             }
187             my %direct_delayed_loads = (
188             # This hash lists all the files that may be loaded in
189             # a delayed fashion by files that may be loaded as a
190             # result of loading this module or which may be loaded
191             # too early to get this module in first. Delayed loading
192             # refers to loading by means of a "require" that is not
193             # executed during loading of the file containing the
194             # "require". The significance of that is that such a
195             # "require" may have been compiled before we installed
196             # the workaround, thus being vulnerable to hint leakage,
197             # and is liable to be executed later when some hints
198             # have actually been set.
199             "AutoLoader.pm" => [
200             # AutoLoader has a specific delayed load of
201             # Carp.pm, and no other specific delayed loads,
202             # but it also performs delayed loads of an
203             # open-ended set of files. Doing so is its
204             # core purpose. This situation can't be dealt
205             # with by the preemptive loading that this hash
206             # supports, and needs its own handling (above).
207             ],
208             "B.pm" => [],
209             "Carp.pm" => [qw(Carp/Heavy.pm)],
210             "Carp/Heavy.pm" => [],
211             "Config.pm" => ["$]" >= 5.008007 ? qw(Config_heavy.pl) : ()],
212             "Config_git.pl" => [],
213             "Config_heavy.pl" => [
214             ("$]" >= 5.010001 ? qw(Config_git.pl) : ()),
215             ],
216             "DynaLoader.pm" => [qw(Carp.pm)],
217             "Exporter.pm" => [qw(Carp.pm Exporter/Heavy.pm)],
218             "Exporter/Heavy.pm" => [qw(Carp.pm)],
219             "List/Util.pm" => [],
220             "List/Util/PP.pm" => [qw(Carp.pm Scalar/Util.pm)],
221             "Mac/FileSpec/Unixish.pm" => [],
222             "Scalar/Util.pm" => [qw(Carp.pm)],
223             "Scalar/Util/PP.pm" => [qw(overload.pm)],
224             "XSLoader.pm" => [qw(Carp.pm DynaLoader.pm)],
225             "feature.pm" => [qw(Carp.pm)],
226             "mro.pm" => [],
227             "overload.pm" => [
228             ("$]" >= 5.008001 ? qw(Scalar/Util.pm) : ()),
229             ("$]" >= 5.011000 ? qw(mro.pm) : ()),
230             ],
231             "overload/numbers.pm" => [],
232             "overloading.pm" => [qw(overload/numbers.pm)],
233             "strict.pm" => [qw(Carp.pm)],
234             "utf8.pm" => [qw(Carp.pm utf8_heavy.pl)],
235             "utf8_heavy.pl" => [
236             # utf8_heavy.pl has a specific delayed load of
237             # Carp.pm, but it also performs delayed loads
238             # of an open-ended set of files. This situation
239             # can't be dealt with by the preemptive loading
240             # that this hash supports, and needs its own
241             # handling (above).
242             ],
243             "vars.pm" => [qw(Carp.pm)],
244             "warnings.pm" => [qw(Carp.pm Carp/Heavy.pm)],
245             "warnings/register.pm" => [],
246             );
247             foreach my $already (sort keys %INC) {
248             foreach my $need (@{$direct_delayed_loads{$already} || []}) {
249             # Loading the target file now means that if the
250             # vulnerable "require" executes later then it
251             # won't actually be causing file loading, so no
252             # hint leakage will happen. This "require" is
253             # itself vulnerable, but so are all the "require"s
254             # that happened immediately during loading of
255             # this module; we expect that this module is
256             # loaded early enough that there are no hints set
257             # that would be a problem. Because we're doing
258             # this loading after installing the workaround,
259             # the target file's "require"s won't themselves
260             # be vulnerable, so we don't need to recurse.
261             scalar(require($need));
262             }
263             }
264             $install_full_workaround_idempotently = sub {};
265             };
266              
267             if("$]" >= 5.012) {
268             # bug not present
269             $install_full_workaround_idempotently = sub {};
270             } elsif(eval { local $SIG{__DIE__};
271             require XSLoader;
272             XSLoader::load(__PACKAGE__, $VERSION);
273             1;
274             }) {
275             # successfully loaded XS
276             $install_compilation_workaround = \&_install_compilation_workaround;
277             } elsif("$]" < 5.007002) {
278             die "pure Perl version of @{[__PACKAGE__]} can't work on pre-5.8 perl";
279             } elsif("$]" >= 5.009004 && "$]" < 5.010001) {
280             die "pure Perl version of @{[__PACKAGE__]} can't work on perl 5.10.0";
281             } else {
282             $install_compilation_workaround = sub {
283             my $next_require = defined(&CORE::GLOBAL::require) ?
284             \&CORE::GLOBAL::require : sub {
285             my($arg) = @_;
286             # The shenanigans with $CORE::GLOBAL::{require}
287             # are required because if there's a
288             # &CORE::GLOBAL::require when the eval is
289             # executed (compiling the CORE::require it
290             # contains) then the CORE::require in there is
291             # interpreted as plain require on some Perl
292             # versions, leading to recursion.
293             my $grequire = $CORE::GLOBAL::{require};
294             delete $CORE::GLOBAL::{require};
295             my $requirer = eval qq{
296             package @{[scalar(caller(0))]};
297             sub { scalar(CORE::require(\$_[0])) };
298             };
299             $CORE::GLOBAL::{require} = $grequire;
300             return scalar($requirer->($arg));
301             };
302 27     27   199 no warnings qw(redefine prototype);
  27         69  
  27         7508  
303             *CORE::GLOBAL::require = sub ($) {
304             die "wrong number of arguments to require\n"
305             unless @_ == 1;
306             my($arg) = @_;
307             # Some reference to $next_require is required
308             # at this level of subroutine so that it will
309             # be closed over and hence made available to
310             # the string eval.
311             my $nr = $next_require;
312             my $requirer = eval qq{
313             package @{[scalar(caller(0))]};
314             sub { scalar(\$next_require->(\$_[0])) };
315             };
316             # We must localise %^H when performing a require
317             # with a filename, but not a require with a
318             # version number. This is because on Perl 5.9.5
319             # and above require with a version number does an
320             # internal importation from the "feature" module,
321             # which is intentional behaviour that must be
322             # allowed to affect %^H. (That's logically the
323             # wrong place for the feature importation, but
324             # it's too late to change how old Perls do it.)
325             # A version number is an argument that is either
326             # numeric or, from Perl 5.9.2 onwards, a v-string.
327             my $must_localise = ($arg^$arg) ne "0" &&
328             !("$]" >= 5.009002 && ref(\$arg) eq "VSTRING");
329             # On Perl 5.11 we need to set the HINT_LOCALIZE_HH
330             # bit to get proper restoration of %^H by the
331             # swash loading code.
332             $^H |= 0x20000 if "$]" >= 5.011 && $must_localise;
333             # Compile-time %^H gets localised by the
334             # "local %^H". Runtime %^H doesn't exist prior
335             # to Perl 5.9.4, and on Perl 5.10.1 and above is
336             # correctly localised by require. Between those
337             # two regimes there's an area where we can't
338             # correctly localise runtime %^H in pure Perl,
339             # short of putting an eval frame around the
340             # require, so we don't use this implementation in
341             # that region.
342             local %^H if $must_localise;
343             return scalar($requirer->($arg));
344             };
345             my $next_do = defined(&CORE::GLOBAL::do) ?
346             \&CORE::GLOBAL::do : sub {
347             my($arg) = @_;
348             my $gdo = $CORE::GLOBAL::{do};
349             delete $CORE::GLOBAL::{do};
350             my $doer = eval qq{
351             package @{[scalar(caller(0))]};
352             sub { CORE::do(\$_[0]) };
353             };
354             $CORE::GLOBAL::{do} = $gdo;
355             return $doer->($arg);
356             };
357 27     27   184 no warnings qw(redefine prototype);
  27         64  
  27         6422  
358             *CORE::GLOBAL::do = sub ($) {
359             die "wrong number of arguments to do\n"
360             unless @_ == 1;
361             my($arg) = @_;
362             my $nd = $next_do;
363             my $doer = eval qq{
364             package @{[scalar(caller(0))]};
365             sub { \$next_do->(\$_[0]) };
366             };
367             $^H |= 0x20000 if "$]" >= 5.011;
368             local %^H;
369             return $doer->($arg);
370             };
371             };
372             }
373              
374             sub import {
375 31 50   31   116303 die "$_[0] does not take any importation arguments\n"
376             unless @_ == 1;
377 31         254 $install_full_workaround_idempotently->();
378 31         2243 return;
379             }
380              
381             sub unimport {
382 0     0     die "$_[0] does not support unimportation\n";
383             }
384              
385             =head1 BUGS
386              
387             The operation of this module depends on influencing the compilation
388             of C and C. As a result, it cannot prevent lexical state
389             leakage through a C/C statement that was compiled before
390             this module was invoked. Where problems occur, this module must be
391             invoked earlier.
392              
393             On all Perl versions that need a fix for the lexical hint leakage bug,
394             the pure Perl implementation of this module unavoidably breaks the use
395             of C without an explicit parameter (implicitly using C<$_>).
396             This is due to another bug in the Perl core, fixed in Perl 5.15.5, and is
397             inherent to the mechanism by which pure Perl code can hook C.
398             The use of implicit C<$_> with C is rare, so although this
399             state of affairs is faulty it will actually work for most programs.
400             Perl versions 5.12.0 and greater, despite having the C hooking
401             bug, don't actually exhibit a problem with the pure Perl version of this
402             module, because with the lexical hint leakage bug fixed there is no need
403             for this module to hook C.
404              
405             There is a bug on Perl versions 5.15.5 to 5.15.7 affecting C which,
406             among other effects, causes C<%^H> to leak into Ced files. It is
407             not the same bug that affected Perl 5.6 to 5.11. This module currently
408             does not work around this bug at all, but its test suite does detect it.
409             As a result, this module fails its test suite on those Perl versions.
410             This could change in future versions of this module.
411              
412             =head1 SEE ALSO
413              
414             L
415              
416             =head1 AUTHOR
417              
418             Andrew Main (Zefram)
419              
420             =head1 COPYRIGHT
421              
422             Copyright (C) 2009, 2010, 2011, 2012, 2015, 2016, 2017, 2023
423             Andrew Main (Zefram)
424              
425             =head1 LICENSE
426              
427             This module is free software; you can redistribute it and/or modify it
428             under the same terms as Perl itself.
429              
430             =cut
431              
432             1;