Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Passing argument into STDIN inside safe.pm reval

by gideondsouza (Pilgrim)
on Feb 11, 2013 at 18:33 UTC ( [id://1018212]=perlquestion: print w/replies, xml ) Need Help??

gideondsouza has asked for the wisdom of the Perl Monks concerning the following question:

So I have a little 'executor.pl' script that looks like this:

## executor.pl use Safe; if($ARGV[0]) { my $code = $ARGV[0]; my $compartment = new Safe; $compartment->deny(qw(:base_io... MANY OP CODES....)); $compartment->permit(qw(print say pack unpack require caller)); ##I want to pass something into the STDIN for the $code reval- +ed my $result = $compartment->reval($code); if ($@) { print "Unsafe code detected: $@"; } }

And I do this to invoke executor.pl :

open(FILE,"perl executor.pl '$code' 2>&1 |") my @output=<FILE>; my $resp = join('',@output);

Question: Is there a way I can pass some argument into the code being reval-ed as STDIN ?. So essentially I can reval some code and then pass it some known arg to its STDIN

Please don't worry, this isn't any production code. I'm just doing this as a learning/curiosity exercise

Replies are listed 'Best First'.
Re: Passing argument into STDIN inside safe.pm reval
by 7stud (Deacon) on Feb 12, 2013 at 02:10 UTC

    Actually, Safe has a share() method, which you should be able to use on STDIN--but it doesn't work for me:

    use strict; use warnings; use 5.012; use Safe; if($ARGV[0]) { my $code = $ARGV[0]; my $compartment = Safe->new; $compartment->share('*STDIN'); $compartment->permit_only(qw{ print readline }); $compartment->reval($code); if ($@) { print "Unsafe code detected: $@"; } } --output:-- $ perl 2.pl ' print "Enter some stdin: "; my $line = readline *STDIN; print "From STDIN: $line"; ' Unsafe code detected: 'private value' trapped by operation mask at (ev +al 5) line 1.

    I can't even get share() to work on a simple scalar variable:

    use strict; use warnings; use 5.012; use Safe; our $x = 10; if($ARGV[0]) { my $code = $ARGV[0]; my $compartment = Safe->new; $compartment->share('$x'); $compartment->permit_only(qw{ print readline}); $compartment->reval($code); if ($@) { print "Unsafe code detected: $@"; } } --output:-- $ perl 2.pl 'print "$x\n";' Unsafe code detected: 'private value' trapped by operation mask at (ev +al 5) line 1.

    I'm really sick of that non-informative error message!

      Hey thanks a ton for your replies and effort!

      I guess running the code in a virtual environment is the only way out!

      I'm trying to sort of understand and mimic www.perltuts.com. The author says this on his blog :

      Evaluator is a simple daemon that runs on a virtual machine, it accepts the code, builds the Perl package and evals it capturing the output with Capture::Tiny. It does some timeout checking and fork limiting of course. The virtual machine (qemu Debian image) allows you to run a real Perl code on a real machine. This can be used when writing advanced tutorials including IO, networking, forks etc (I tried Safe but wanted more freedom). It is reset every hour from a snaphot.

      So I' playing about on Amazon EC2 and was thinking of running a new instance that will just evaluate code and return the result. I'm thinking I can use Time::Out to timeout operations, but (1) how do I do a fork limit? (2) Am I pretty safe if I allow any perl code to run but restart my VM every hour or something?

      I've tried to replace permit_only with permit in your example, and it worked for me.

      Still need to figure out what should I place into permit_only so that it allows to eval '1' (which is obviously safe).

        That's confounding. I printed out the masks for both permit(qw(print readline)) and permit_only(qw(print readline)) using printf():
        printf "%vd \n", $compartment->mask;

        ...and they are clearly different, which means a compartment allows some default operations that permit_only() must be erasing. Rereading the Safe docs, there is a set of default operations bundled under :default that are allowed. You have to check the Opcode docs to see which operations that :default includes. I'm not sure which ones I erased with permit_only() that are needed to reval() my sample code--but in any case it looks like if you want to use permit_only(), then you probably need a pretty good grasp of perl internals.

        I did a bunch of trial and error, and to get permit_only() to work on my sample code, I need all these:

        $compartment->permit_only(qw( print readline :base_core :base_mem :base_orig) );
        The :default bundle of operations that a compartment allows consists of a bevy of other bundles:

        :base_core 
        :base_mem 
        :base_loop 
        :base_orig 
        :base_thread
        

        See the Opcode docs for which operations are included in each of those bundles.

Re: Passing argument into STDIN inside safe.pm reval
by 7stud (Deacon) on Feb 11, 2013 at 19:22 UTC

    I don't think a string has a STDIN filehandle. After you eval the string, it becomes part of your program. Your program has a STDIN filehandle.

    Although, you can perform IO on a string (using open).

    So essentially I can reval some code and then pass it some known arg to its STDIN

    How about using root to insert the input?

Re: Passing argument into STDIN inside safe.pm reval
by 7stud (Deacon) on Feb 12, 2013 at 00:12 UTC

    You can insert your program's STDIN filehandle into the compartment like this:

    use strict; use warnings; use 5.012; use Safe; if($ARGV[0]) { my $code = $ARGV[0]; my $compartment = Safe->new; my $namespace_name = $compartment->root(); { no strict; *{$namespace_name . "::STDIN"} = *main::STDIN; } $compartment->deny_only(qw(chdir)); #<----CHECK THIS*** $compartment->reval($code); if ($@) { print "Unsafe code detected: $@"; } } --output:-- $ $ perl 2.pl ' print "Enter some stdin: "; my $line = <STDIN>; print "From STDIN: $line"; ' Enter some stdin: hello From STDIN: hello

    But I can't figure out how to permit_only() what is necessary for the line input operator(<$infile>) to work. I tried permit_only(qw{ print <> }), which produced the error:

    Unknown operator prefix "<>"

    And because perlop says:

    <FILEHANDLE> may also be spelled readline(*FILEHANDLE). 

    I tried permit_only(qw{ print readline }), but that produced this error:

    Unsafe code detected: 'private value' trapped by operation mask at (ev +al 5) line 1. use strict; use warnings; use 5.012; use Safe; if($ARGV[0]) { my $code = $ARGV[0]; my $compartment = Safe->new; my $namespace_name = $compartment->root(); { no strict; *{$namespace_name . "::STDIN"} = *main::STDIN; } $compartment->permit_only(qw(print readline)); $compartment->reval($code); if ($@) { print "Unsafe code detected: $@"; } } --output:-- $ perl 2.pl ' print "Enter some stdin: "; my $line = readline *STDIN; print "From STDIN: $line"; ' Unsafe code detected: 'private value' trapped by operation mask at (ev +al 5) line 1.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1018212]
Front-paged by Lotus1
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (3)
As of 2024-04-26 01:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found