File Coverage

blib/lib/Eval/Safe/ForkedSafe.pm
Criterion Covered Total %
statement 103 201 51.2
branch 23 84 27.3
condition 4 20 20.0
subroutine 23 42 54.7
pod 0 23 0.0
total 153 370 41.3


line stmt bran cond sub pod time code
1             # This module is a fork of the standard Perl module Safe 2.40. This fork intend
2             # to fix the following bug: https://rt.perl.org/Ticket/Display.html?id=134459
3             #
4             # This module is originally designed and implemented by Malcolm Beattie.
5             # Reworked to use the Opcode module and other changes added by Tim Bunce.
6             # Currently maintained by the Perl 5 Porters, .
7             #
8             # This fork and all the bug that it adds is by Mathias Kende .
9             #
10             # The code here is released under the Perl Artistic Licence:
11             # https://dev.perl.org/licenses/artistic.html
12             #
13             # And the diff between this module and the original Safe one is released in the
14             # public domain (or free to use by anyone in any way whatsoever).
15             #
16             # Changelog, compared to Safe 2.40:
17             # - in wrap_code_ref, wrap the wrapped_code in an eval to catch exceptions;
18             # - do not raise these exceptions, let the user read them in $@;
19             # - recursively wrap code returned by wrapped code;
20             # - propagate an undef wantarray when running wrapped code;
21             # - rename the package to Eval::Safe::ForkedSafe (adapt the `isa` tests as
22             # well as the default root namespace).
23             # - bump the version to 2.41.
24            
25             package Eval::Safe::ForkedSafe;
26            
27 3     3   42 use 5.003_11;
  3         10  
28 3     3   14 use Scalar::Util qw(reftype refaddr);
  3         6  
  3         488  
29            
30             $Safe::VERSION = "2.41";
31            
32             # *** Don't declare any lexicals above this point ***
33             #
34             # This function should return a closure which contains an eval that can't
35             # see any lexicals in scope (apart from __ExPr__ which is unavoidable)
36            
37             sub lexless_anon_sub {
38             # $_[0] is package;
39             # $_[1] is strict flag;
40 0     0 0 0 my $__ExPr__ = $_[2]; # must be a lexical to create the closure that
41             # can be used to pass the value into the safe
42             # world
43            
44             # Create anon sub ref in root of compartment.
45             # Uses a closure (on $__ExPr__) to pass in the code to be executed.
46             # (eval on one line to keep line numbers as expected by caller)
47 0 0       0 eval sprintf
48             'package %s; %s sub { @_=(); eval q[local *SIG; my $__ExPr__;] . $__ExPr__; }',
49             $_[0], $_[1] ? 'use strict;' : '';
50             }
51            
52 3     3   18 use strict;
  3         3  
  3         61  
53 3     3   19 use Carp;
  3         5  
  3         133  
54 3     3   172 BEGIN { eval q{
  3     3   1402  
  3         489  
  3         109  
55             use Carp::Heavy;
56             } }
57            
58 3     3   17 use B ();
  3         6  
  3         55  
59             BEGIN {
60 3     3   13 no strict 'refs';
  3         6  
  3         197  
61 3 50   3   15 if (defined &B::sub_generation) {
62 3         73 *sub_generation = \&B::sub_generation;
63             }
64             else {
65             # fake sub generation changing for perls < 5.8.9
66 0         0 my $sg; *sub_generation = sub { ++$sg };
  0         0  
  0         0  
67             }
68             }
69            
70 3         1677 use Opcode 1.01, qw(
71             opset opset_to_ops opmask_add
72             empty_opset full_opset invert_opset verify_opset
73             opdesc opcodes opmask define_optag opset_to_hex
74 3     3   3306 );
  3         15237  
75            
76             *ops_to_opset = \&opset; # Temporary alias for old Penguins
77            
78             # Regular expressions and other unicode-aware code may need to call
79             # utf8->SWASHNEW (via perl's utf8.c). That will fail unless we share the
80             # SWASHNEW method.
81             # Sadly we can't just add utf8::SWASHNEW to $default_share because perl's
82             # utf8.c code does a fetchmethod on SWASHNEW to check if utf8.pm is loaded,
83             # and sharing makes it look like the method exists.
84             # The simplest and most robust fix is to ensure the utf8 module is loaded when
85             # Safe is loaded. Then we can add utf8::SWASHNEW to $default_share.
86             require utf8;
87             # we must ensure that utf8_heavy.pl, where SWASHNEW is defined, is loaded
88             # but without depending on too much knowledge of that implementation detail.
89             # This code (//i on a unicode string) should ensure utf8 is fully loaded
90             # and also loads the ToFold SWASH, unless things change so that these
91             # particular code points don't cause it to load.
92             # (Swashes are cached internally by perl in PL_utf8_* variables
93             # independent of being inside/outside of Safe. So once loaded they can be)
94 3     3   14 do { my $a = pack('U',0x100); my $b = chr 0x101; utf8::upgrade $b; $a =~ /$b/i };
  3         4  
  3         26  
95             # now we can safely include utf8::SWASHNEW in $default_share defined below.
96            
97             my $default_root = 0;
98             # share *_ and functions defined in universal.c
99             # Don't share stuff like *UNIVERSAL:: otherwise code from the
100             # compartment can 0wn functions in UNIVERSAL
101             my $default_share = [qw[
102             *_
103             &PerlIO::get_layers
104             &UNIVERSAL::isa
105             &UNIVERSAL::can
106             &UNIVERSAL::VERSION
107             &utf8::is_utf8
108             &utf8::valid
109             &utf8::encode
110             &utf8::decode
111             &utf8::upgrade
112             &utf8::downgrade
113             &utf8::native_to_unicode
114             &utf8::unicode_to_native
115             &utf8::SWASHNEW
116             $version::VERSION
117             $version::CLASS
118             $version::STRICT
119             $version::LAX
120             @version::ISA
121             ], ($] < 5.010 && qw[
122             &utf8::SWASHGET
123             ]), ($] >= 5.008001 && qw[
124             &Regexp::DESTROY
125             ]), ($] >= 5.010 && qw[
126             &re::is_regexp
127             &re::regname
128             &re::regnames
129             &re::regnames_count
130             &UNIVERSAL::DOES
131             &version::()
132             &version::new
133             &version::(""
134             &version::stringify
135             &version::(0+
136             &version::numify
137             &version::normal
138             &version::(cmp
139             &version::(<=>
140             &version::vcmp
141             &version::(bool
142             &version::boolean
143             &version::(nomethod
144             &version::noop
145             &version::is_alpha
146             &version::qv
147             &version::vxs::declare
148             &version::vxs::qv
149             &version::vxs::_VERSION
150             &version::vxs::stringify
151             &version::vxs::new
152             &version::vxs::parse
153             &version::vxs::VCMP
154             ]), ($] >= 5.011 && qw[
155             &re::regexp_pattern
156             ]), ($] >= 5.010 && $] < 5.014 && qw[
157             &Tie::Hash::NamedCapture::FETCH
158             &Tie::Hash::NamedCapture::STORE
159             &Tie::Hash::NamedCapture::DELETE
160             &Tie::Hash::NamedCapture::CLEAR
161             &Tie::Hash::NamedCapture::EXISTS
162             &Tie::Hash::NamedCapture::FIRSTKEY
163             &Tie::Hash::NamedCapture::NEXTKEY
164             &Tie::Hash::NamedCapture::SCALAR
165             &Tie::Hash::NamedCapture::flags
166             ])];
167             if (defined $Devel::Cover::VERSION) {
168             push @$default_share, '&Devel::Cover::use_file';
169             }
170            
171             sub new {
172 1     1 0 4 my($class, $root, $mask) = @_;
173 1         3 my $obj = {};
174 1         1 bless $obj, $class;
175            
176 1 50       3 if (defined($root)) {
177 0 0 0     0 croak "Can't use \"$root\" as root name"
178             if $root =~ /^main\b/ or $root !~ /^\w[:\w]*$/;
179 0         0 $obj->{Root} = $root;
180 0         0 $obj->{Erase} = 0;
181             }
182             else {
183 1         6 $obj->{Root} = "Eval::Safe::ForkedSafe::Root".$default_root++;
184 1         2 $obj->{Erase} = 1;
185             }
186            
187             # use permit/deny methods instead till interface issues resolved
188             # XXX perhaps new Safe 'Root', mask => $mask, foo => bar, ...;
189 1 50       4 croak "Mask parameter to new no longer supported" if defined $mask;
190 1         5 $obj->permit_only(':default');
191            
192             # We must share $_ and @_ with the compartment or else ops such
193             # as split, length and so on won't default to $_ properly, nor
194             # will passing argument to subroutines work (via @_). In fact,
195             # for reasons I don't completely understand, we need to share
196             # the whole glob *_ rather than $_ and @_ separately, otherwise
197             # @_ in non default packages within the compartment don't work.
198 1         5 $obj->share_from('main', $default_share);
199            
200 1 50       12 Opcode::_safe_pkg_prep($obj->{Root}) if($Opcode::VERSION > 1.04);
201            
202 1         3 return $obj;
203             }
204            
205             sub DESTROY {
206 1     1   2 my $obj = shift;
207 1 50       8 $obj->erase('DESTROY') if $obj->{Erase};
208             }
209            
210             sub erase {
211 1     1 0 4 my ($obj, $action) = @_;
212 1         3 my $pkg = $obj->root();
213 1         2 my ($stem, $leaf);
214            
215 3     3   23 no strict 'refs';
  3         5  
  3         1528  
216 1         3 $pkg = "main::$pkg\::"; # expand to full symbol table name
217 1         7 ($stem, $leaf) = $pkg =~ m/(.*::)(\w+::)$/;
218            
219             # The 'my $foo' is needed! Without it you get an
220             # 'Attempt to free unreferenced scalar' warning!
221 1         3 my $stem_symtab = *{$stem}{HASH};
  1         3  
222            
223             #warn "erase($pkg) stem=$stem, leaf=$leaf";
224             #warn " stem_symtab hash ".scalar(%$stem_symtab)."\n";
225             # ", join(', ', %$stem_symtab),"\n";
226            
227             # delete $stem_symtab->{$leaf};
228            
229 1         4 my $leaf_glob = $stem_symtab->{$leaf};
230 1         2 my $leaf_symtab = *{$leaf_glob}{HASH};
  1         2  
231             # warn " leaf_symtab ", join(', ', %$leaf_symtab),"\n";
232 1         26 %$leaf_symtab = ();
233             #delete $leaf_symtab->{'__ANON__'};
234             #delete $leaf_symtab->{'foo'};
235             #delete $leaf_symtab->{'main::'};
236             # my $foo = undef ${"$stem\::"}{"$leaf\::"};
237            
238 1 50 33     10 if ($action and $action eq 'DESTROY') {
239 1         6 delete $stem_symtab->{$leaf};
240             } else {
241 0         0 $obj->share_from('main', $default_share);
242             }
243 1         12 1;
244             }
245            
246            
247             sub reinit {
248 0     0 0 0 my $obj= shift;
249 0         0 $obj->erase;
250 0         0 $obj->share_redo;
251             }
252            
253             sub root {
254 3     3 0 5 my $obj = shift;
255 3 50       6 croak("Safe root method now read-only") if @_;
256 3         7 return $obj->{Root};
257             }
258            
259            
260             sub mask {
261 0     0 0 0 my $obj = shift;
262 0 0       0 return $obj->{Mask} unless @_;
263 0         0 $obj->deny_only(@_);
264             }
265            
266             # v1 compatibility methods
267 0     0 0 0 sub trap { shift->deny(@_) }
268 0     0 0 0 sub untrap { shift->permit(@_) }
269            
270             sub deny {
271 0     0 0 0 my $obj = shift;
272 0         0 $obj->{Mask} |= opset(@_);
273             }
274             sub deny_only {
275 1     1 0 2 my $obj = shift;
276 1         7 $obj->{Mask} = opset(@_);
277             }
278            
279             sub permit {
280 0     0 0 0 my $obj = shift;
281             # XXX needs testing
282 0         0 $obj->{Mask} &= invert_opset opset(@_);
283             }
284             sub permit_only {
285 1     1 0 2 my $obj = shift;
286 1         8 $obj->{Mask} = invert_opset opset(@_);
287             }
288            
289            
290             sub dump_mask {
291 0     0 0 0 my $obj = shift;
292 0         0 print opset_to_hex($obj->{Mask}),"\n";
293             }
294            
295            
296             sub share {
297 0     0 0 0 my($obj, @vars) = @_;
298 0         0 $obj->share_from(scalar(caller), \@vars);
299             }
300            
301            
302             sub share_from {
303 1     1 0 2 my $obj = shift;
304 1         2 my $pkg = shift;
305 1         1 my $vars = shift;
306 1   50     5 my $no_record = shift || 0;
307 1         3 my $root = $obj->root();
308 1 50       4 croak("vars not an array ref") unless ref $vars eq 'ARRAY';
309 3     3   21 no strict 'refs';
  3         5  
  3         1231  
310             # Check that 'from' package actually exists
311             croak("Package \"$pkg\" does not exist")
312 1 50       1 unless keys %{"$pkg\::"};
  1         5  
313 1         1 my $arg;
314 1         3 foreach $arg (@$vars) {
315             # catch some $safe->share($var) errors:
316 52         62 my ($var, $type);
317 52 100       159 $type = $1 if ($var = $arg) =~ s/^(\W)//;
318             # warn "share_from $pkg $type $var";
319 52         84 for (1..2) { # assign twice to avoid any 'used once' warnings
320 104         333 *{$root."::$var"} = (!$type) ? \&{$pkg."::$var"}
  4         10  
321 88         228 : ($type eq '&') ? \&{$pkg."::$var"}
322 8         53 : ($type eq '$') ? \${$pkg."::$var"}
323 2         23 : ($type eq '@') ? \@{$pkg."::$var"}
324 0         0 : ($type eq '%') ? \%{$pkg."::$var"}
325 104 50       170 : ($type eq '*') ? *{$pkg."::$var"}
  2 50       6  
    100          
    100          
    100          
    100          
326             : croak(qq(Can't share "$type$var" of unknown type));
327             }
328             }
329 1 50 33     9 $obj->share_record($pkg, $vars) unless $no_record or !$vars;
330             }
331            
332            
333             sub share_record {
334 1     1 0 2 my $obj = shift;
335 1         2 my $pkg = shift;
336 1         2 my $vars = shift;
337 1   50     1 my $shares = \%{$obj->{Shares} ||= {}};
  1         8  
338             # Record shares using keys of $obj->{Shares}. See reinit.
339 1 50       5 @{$shares}{@$vars} = ($pkg) x @$vars if @$vars;
  1         36  
340             }
341            
342            
343             sub share_redo {
344 0     0 0   my $obj = shift;
345 0   0       my $shares = \%{$obj->{Shares} ||= {}};
  0            
346 0           my($var, $pkg);
347 0           while(($var, $pkg) = each %$shares) {
348             # warn "share_redo $pkg\:: $var";
349 0           $obj->share_from($pkg, [ $var ], 1);
350             }
351             }
352            
353            
354             sub share_forget {
355 0     0 0   delete shift->{Shares};
356             }
357            
358            
359             sub varglob {
360 0     0 0   my ($obj, $var) = @_;
361 3     3   19 no strict 'refs';
  3         5  
  3         224  
362 0           return *{$obj->root()."::$var"};
  0            
363             }
364            
365             sub _clean_stash {
366 0     0     my ($root, $saved_refs) = @_;
367 0   0       $saved_refs ||= [];
368 3     3   17 no strict 'refs';
  3         4  
  3         2329  
369 0           foreach my $hook (qw(DESTROY AUTOLOAD), grep /^\(/, keys %$root) {
370 0           push @$saved_refs, \*{$root.$hook};
  0            
371 0           delete ${$root}{$hook};
  0            
372             }
373            
374 0           for (grep /::$/, keys %$root) {
375 0 0         next if \%{$root.$_} eq \%$root;
  0            
376 0           _clean_stash($root.$_, $saved_refs);
377             }
378             }
379            
380             sub reval {
381 0     0 0   my ($obj, $expr, $strict) = @_;
382 0 0         die "Bad Safe object" unless $obj->isa('Eval::Safe::ForkedSafe');
383            
384 0           my $root = $obj->{Root};
385            
386 0           my $evalsub = lexless_anon_sub($root, $strict, $expr);
387             # propagate context
388 0           my $sg = sub_generation();
389 0           my @subret;
390 0 0         if (defined wantarray) {
391             @subret = (wantarray)
392             ? Opcode::_safe_call_sv($root, $obj->{Mask}, $evalsub)
393 0 0         : scalar Opcode::_safe_call_sv($root, $obj->{Mask}, $evalsub);
394             }
395             else {
396 0           Opcode::_safe_call_sv($root, $obj->{Mask}, $evalsub);
397             }
398 0 0         _clean_stash($root.'::') if $sg != sub_generation();
399 0           $obj->wrap_code_refs_within(@subret);
400 0 0         return (wantarray) ? @subret : $subret[0];
401             }
402            
403             my %OID;
404            
405             sub wrap_code_refs_within {
406 0     0 0   my $obj = shift;
407            
408 0           %OID = ();
409 0           $obj->_find_code_refs('wrap_code_ref', @_);
410             }
411            
412            
413             sub _find_code_refs {
414 0     0     my $obj = shift;
415 0           my $visitor = shift;
416            
417 0           for my $item (@_) {
418 0 0 0       my $reftype = $item && reftype $item
419             or next;
420            
421             # skip references already seen
422 0 0         next if ++$OID{refaddr $item} > 1;
423            
424 0 0         if ($reftype eq 'ARRAY') {
    0          
    0          
425 0           $obj->_find_code_refs($visitor, @$item);
426             }
427             elsif ($reftype eq 'HASH') {
428 0           $obj->_find_code_refs($visitor, values %$item);
429             }
430             # XXX GLOBs?
431             elsif ($reftype eq 'CODE') {
432 0           $item = $obj->$visitor($item);
433             }
434             }
435             }
436            
437            
438             sub wrap_code_ref {
439 0     0 0   my ($obj, $sub) = @_;
440 0 0         die "Bad safe object" unless $obj->isa('Eval::Safe::ForkedSafe');
441            
442             # wrap code ref $sub with _safe_call_sv so that, when called, the
443             # execution will happen with the compartment fully 'in effect'.
444            
445 0 0         croak "Not a CODE reference"
446             if reftype $sub ne 'CODE';
447            
448             my $ret = sub {
449 0     0     my @args = @_; # lexical to close over
450 0           my $sub_with_args = sub { eval { $sub->(@args) } };
  0            
  0            
451            
452 0           my @subret;
453             my $error;
454 0           do {
455 0           my $sg = sub_generation();
456 0 0         if (defined wantarray) {
457             @subret = (wantarray)
458             ? Opcode::_safe_call_sv($obj->{Root}, $obj->{Mask}, $sub_with_args)
459 0 0         : scalar Opcode::_safe_call_sv($obj->{Root}, $obj->{Mask}, $sub_with_args);
460             } else {
461 0           Opcode::_safe_call_sv($obj->{Root}, $obj->{Mask}, $sub_with_args);
462             }
463 0 0         _clean_stash($obj->{Root}.'::') if $sg != sub_generation();
464             };
465 0           $obj->wrap_code_refs_within(@subret);
466 0 0         return (wantarray) ? @subret : $subret[0];
467 0           };
468            
469 0           return $ret;
470             }
471            
472            
473             sub rdo {
474 0     0 0   my ($obj, $file) = @_;
475 0 0         die "Bad Safe object" unless $obj->isa('Eval::Safe::ForkedSafe');
476            
477 0           my $root = $obj->{Root};
478            
479 0           my $sg = sub_generation();
480 0           my $evalsub = eval
481             sprintf('package %s; sub { @_ = (); do $file }', $root);
482             my @subret = (wantarray)
483             ? Opcode::_safe_call_sv($root, $obj->{Mask}, $evalsub)
484 0 0         : scalar Opcode::_safe_call_sv($root, $obj->{Mask}, $evalsub);
485 0 0         _clean_stash($root.'::') if $sg != sub_generation();
486 0           $obj->wrap_code_refs_within(@subret);
487 0 0         return (wantarray) ? @subret : $subret[0];
488             }
489            
490            
491             1;