Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

The safety of string eval and block eval.

by TrixieTang (Sexton)
on Aug 14, 2016 at 20:33 UTC ( [id://1169762]=perlquestion: print w/replies, xml ) Need Help??

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

I'm fairly new to Perl and I'm currently trying to understand the two forms of eval.

Everything that I read about them says that string eval is dangerous and block eval is "safe" - But from what I understand, both are capable of executing code that results from potentially dangerous user input

What makes the block form "safe" of eval compared to the string form?

  • Comment on The safety of string eval and block eval.

Replies are listed 'Best First'.
Re: The safety of string eval and block eval.
by GrandFather (Saint) on Aug 14, 2016 at 21:04 UTC

    String eval does what it implies on the packet - it evaluates a string as though it were Perl source code. Given that that string could include user input there is the very real possibility that malicious user supplied code could be run with whatever credentials your executing script has. Often that's a pretty wide open door to do nasty stuff.

    Block eval on the other hand is much more like the try / catch blocks provided in other languages. It allows a way to manage execution errors generated within the eval. For example:

    my $ok = eval { # code that might call die to signal that something has gone wrong ... return 1; } or do { # error handler code my $error = $@; ... };

    Any die executed within the eval block is handled by the do block allowing error recovery or at least nice error reporting. die and block eval is a very nice way to handle errors without having to hand explicit success back from called code. That helps make code much cleaner and makes it easier to manage error handling correctly.

    Premature optimization is the root of all job security
Re: The safety of string eval and block eval.
by choroba (Cardinal) on Aug 14, 2016 at 21:03 UTC
    Block eval doesn't execute code coming from user input, it executes code literally present in the source. It's used as a poor man's try/catch:
    eval { print "Every command executed"; warn " must be present here "; die @ARGV; 1 } or die $@;

    You can use string eval in a safe way, too, but you must understand exactly what it does, e.g.

    my %ops = ( add => '+', subtract => '-', multiply => '*', divide => '/', ); eval "sub $_ { shift() $ops{$_} shift() }" for keys %ops; print add(4, 6);

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: The safety of string eval and block eval.
by beech (Parson) on Aug 14, 2016 at 21:03 UTC

    But from what I understand, both are capable of executing code that results from potentially dangerous user input

    Try it

    $ perl -le " eval { @ARGV } " print 123 $ perl -le " eval qq{ @ARGV } " print 123 123

    block eval prints nothing, it doesn't turn user input into code

    String eval does

Re: The safety of string eval and block eval.
by Laurent_R (Canon) on Aug 14, 2016 at 22:15 UTC
    There is no problem with string eval if your program controls the string that is going to be evaluated.

    Sometimes, you actually need to evaluate strings hard coded in the program. As an example, let's start with a simple iterator in the spirit of Dominus's Higher Order Perl:

    sub create_iterator{ my $val = shift; return sub { return $val += 2;} } my $iter_even = create_iterator(0); my $iter_odd = create_iterator(1);
    This creates two closures which iterate on even or odd numbers.

    Suppose now that you want to create a generic iterator in which you could pass an arbitrary function. If you try to do that, it might look like this:

    sub create_iter { my ($code_ref, @rest) = @_; return sub {$code_ref->();} }
    and be called with a callback function like this:
    my $even = create_iter(sub {my $c; $c += 2; return $c;}, 0); # Doe +s NOT work
    That does not work because, since the $c variable is created within the callback subroutine passed to the create_iter subroutine, create_iter will not close over the variable which is defined elsewhere.

    Basically, this cannot work because the callback function is not defined within the environment that would make it a closure.

    As far as I can tell, there is no solution with the techniques described above. So, we cannot make a generic iterator? Yes we can, if we use eval to create the callback function at the right place, using a string passed as a parameter. For example, the following code defines a generic iterator used to generate one by one Fibonacci numbers:

    use strict; use warnings; sub create_iter { my ($code_string, @rest) = @_; eval $code_string; } my $fibonacci = create_iter ( ' return sub {my ($c, $d) = @rest; my $e = $c + $d; @rest = ($d, $e); return $e;} ', 1, 1); print "Fibonacci numbers: \n"; print $fibonacci->(), " ", for 1..7;
    which will print:
    Fibonacci: 2 3 5 8 13 21 34
    The point is that using eval makes it possible to create the closure code in the right place for the subroutine to close over the variable.

    I can now use the same generic iterator to generate factorials:

    my $fact = create_iter (<<'EOS' return sub { $rest[0]++; $rest[1] *= $rest[0];}; EOS , 1, 1); print "\n\nFact: \n"; print $fact->(), " ", for 1..5;
    which will print:
    Fact: 2 6 24 120 720
    The example might seem somewhat contrived, but that's the only way that I found to easily create a generic iterator when I needed one.

    So, to me, string eval can in some cases be a very useful technique, and, as far as I can tell, there is no danger of malicious abuse, because the string used is actually hard-coded in the program.

    Another example of useful string eval can be found in the Benchmark module, which offers the possibility to pass the function whose performance should be measured in the form of a string that is (I think) evaluated with eval.

    In brief, my point is that string eval can be very useful and should not be ruled out, but, of course, can be dangerous if used, for example, with user input.

      I'm not sure string eval is needed to create a generic interator, I was able to reproduce all your examples with just anonymous subs and some @_ juggling:
      #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; sub create_iter { my ($code_ref, @rest) = @_; sub { $code_ref->(@rest) } } my $iter = create_iter(sub { $_[0] += 2 }, 0); say $iter->() for 1 .. 5; my $fibo = create_iter(sub { @_[0, 1] = ($_[1], $_[0] + $_[1]); $_[1] +}, 1, 1); say $fibo->() for 1 .. 7; my $fact = create_iter(sub { $_[1] *= ++$_[0] }, 1, 1); say $fact->() for 1 .. 5;
      ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
        Yes choroba, you're right, thanks a lot for your examples. It seems it can be done playing with @_, at least for those examples.

        Either I was simply not able to find the right syntax when I was playing with these things a couple of years ago, or my actual use cases were more complicated than the simple examples I gave in my previous post. I'd have to go back to what I was doing at the time to figure that out, but I do not have these things available with me today.

        My gut feeling, though, is that I probably was simply not able to find the right syntax.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others learning in the Monastery: (2)
As of 2024-04-26 06:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found