3

I already showed that the performance of str2func is better, but I got a lot of comments stating that there are more fundamental reasons to not use eval. Which fundamental reasons do apply to eval and do not apply to str2func in the following situation:

f='a^x+exp(b)+sin(c*x)+d'
  1. eval:

    y = eval(f)
    

    or (suggested by rahnema1)

    fHandle = eval(['@(x, a, b, c, d) ' f]);
    y = fHandle(x, a, b, c, d);
    
  2. str2func:

    fHandle = str2func(['@(x, a, b, c, d) ' f]);
    y = fHandle(x, a, b, c, d);
    

Why is the first option worse than the second one except for performance reasons?

Remarks

  • Note that I'm aware that it is good practice to avoid both methods if possible.

  • Note that I assign the output of eval to a variable, which avoids a lot of tricky code from being executed.

m7913d
  • 10,244
  • 7
  • 28
  • 56
  • 3
    You may want to compare `fHandle = str2func(['@(x, a, b, c, d) ' f]);` and `fHandle = eval(['@(x, a, b, c, d) ' f]);` – rahnema1 Sep 14 '17 at 08:17
  • @rahnema1 I also included your alternative in my [performance benchmark](https://stackoverflow.com/questions/46179940/performance-of-eval-compared-to-str2func-to-evalulate-a-function-from-a-stri) and it comes close to `str2func` for a large number of evaluations. – m7913d Sep 14 '17 at 08:52
  • 4
    If you don't pass a valid function string to `str2func` it will give a warning (or error in future [current?] releases). However, if you don't pass a valid function to `eval` it will do what you ask anyway... imagine `eval('delete C:/*')` as an example of how this could reap horrible results. Obviously you wouldn't *intentionally* make that mistake, but it's an exaggeration of the point that things can go wrong when building function strings (hence why you should avoid both of these where possible) and the more validation checking the better. – Wolfie Sep 14 '17 at 09:06
  • 1
    @Wolfie `y = eval('delete my_file.m')` returns an error (and doesn't delete the file): `Error: Unexpected MATLAB expression.`. (tested with R2016b) – m7913d Sep 14 '17 at 09:14
  • 1
    `eval('!del my_file.m')` will delete `my_file.m` on windows – Steve Sep 14 '17 at 10:01
  • @Steve Also if you write `y = eval('!del my_file.m')`? – m7913d Sep 14 '17 at 10:09
  • 1
    @Wolfie I do not agree. If someone knows a working example with undesired side effects (like deleting a bunch of files) which is not possible with `str2func`, this question has a clear answer. If nobody is able to give such an example, all the comments I received in my previous question are rather wrong/based on the myth _Don't use `eval`_. Both situations are highly relevant and are not opinion based. – m7913d Sep 14 '17 at 10:26
  • 3
    tldr; Both functions should be avoided wherever possible, due to the reasons you're trying to brush aside, so this is a moot point. If you must use `str2func` then use it over `eval` because it has *slightly* more protections built in. – Wolfie Sep 14 '17 at 11:18

4 Answers4

10

Stop trying to downplay the security risk that is eval. Running arbitrary code can be harmful if you don't fully control the input. If you fully control the input, there is almost always a better way that doesn't involve dynamic code generation.

You insist on a specific example:

>> ls TMPFILE
Error using ls (line 35)
ls: cannot access 'TMPFILE': No such file or directory

>> y = eval('system(''touch TMPFILE'')');
>> y

y =

     0

>> ls TMPFILE
TMPFILE

touch is a very friendly unix command; it creates an empty file. Imagine rm -rf ~ in the same context.

Let's try it with str2func:

>> y = str2func('system(''touch TMPFILE2'')');
Warning: The input to STR2FUNC "system('touch TMPFILE2')" is not a valid function name. This will generate an error in a future release. 
>> ls TMPFILE2
Error using ls (line 35)
ls: cannot access 'TMPFILE2': No such file or directory

>> y

y =

  function_handle with value:

    @system('touch TMPFILE2')

>> y()
Undefined function or variable 'system('touch TMPFILE2')'.

Side note: while the risk of eval is real and not just due to superstition, not using eval doesn't necessarily mean you're safe. My favourite example, the sneaky devil of str2num:

>> x = str2num('[system(''touch TMPFILE3'')]')        

x =

     0

>> ls TMPFILE3
TMPFILE3

So even if you're not explicitly using eval, it might happen that a convenience function you're using instead does. You should always make sure that this doesn't happen: use the safe str2double instead of str2num, and use str2func instead of eval (since we saw above that str2func won't execute arbitrary code).

  • Thanks for your answer. I agree avoiding `eval` is a good idea, but I don't get why replacing it with `str2func` makes any difference. You can do exactly the same with `str2func`: `y = str2func('@(x) system(''touch TMPFILE'')'); y(1);` – m7913d Sep 14 '17 at 11:06
  • 4
    @m7913d because for that to work the attacker has to have detailed information about the internals of your program. If you use `eval` or equivalent, any arbitrary `system` command will be executed, no questions asked. Sure, sufficiently sophisticated attacks with sufficient information about the code itself (!) and sufficiently low-quality sanitization efforts from the programmer (!) imply that it's hard to write bullet-proof code if you're using external input. But `str2func` is what will render [Little Bobby Tables harmless](https://xkcd.com/327/). – Andras Deak -- Слава Україні Sep 14 '17 at 11:11
  • @m7913d no? `y = eval('system(''touch TMPFILE'')');` has already created the file. in the `str2func` case no code creates the file or runs the command. – Ander Biguri Sep 14 '17 at 11:12
  • @AndrasDeak In the current use case, both inputs are exactly the same, i.e. `f = 'system(''touch TMPFILE'')'`. Both are equally unsafe in this example. – m7913d Sep 14 '17 at 11:19
  • 1
    @m7913d Ah, I see your point. Yes, it's possible that in this exact use case the two are equally unsafe. But this doesn't imply that the risk of `eval` is unfounded. It rather implies that sometimes you can't be entirely safe. The bottom line should always be to try and write as safe code as possible. In this case, `str2func` is at least as safe as `eval`. It will also save you from malformed (but benign) inputs, for instance `str2func('@(x) 3; 4')` is an error but `eval` will evaluate that. My point is that the default should be _not_ using `eval`, because it _can_ be very unsafe. – Andras Deak -- Слава Україні Sep 14 '17 at 11:23
  • @AnderBiguri I don't get your point, if I use `str2func`, I will evaluate it later, otherwise it is useless to call `str2func`. So, in the end, in both cases the system function will be executed. – m7913d Sep 14 '17 at 11:24
  • 1
    @m7913d go to your question. Imagine you have those 2 lines of code, but instead of `['@(x, a, b, c, d) ' f]` you have `input` (as that would be the case where you would use `eval`). In the `eval` case, the code would run. regardless if the second line errors. in the `str2fun` case, the code would not execute. – Ander Biguri Sep 14 '17 at 11:26
  • @AndrasDeak Up till now, I found no evidence that `str2func` is safer than `eval`. Concerning your last example, `eval` also returns an error for `y = eval('@(x) 3; 4');` I think replacing an unsafe function with an equally unsafe function is just a matter of false safeness. – m7913d Sep 14 '17 at 11:27
  • 3
    @m7913d I'm fairly certain we've given more than enough technical answers for your already ill-founded questions. There's no amount of rational reasoning that will convince you to not use `eval`. I certainly won't continue talking to a brick wall. You're free to use whatever you want, the quality and safety of your code is your sole responsibility. – Andras Deak -- Слава Україні Sep 14 '17 at 11:29
  • @AndrasDeak I only ask for a simple example, which is not (easily) reproducible with `str2func`. Nevertheless, have a nice day. – m7913d Sep 14 '17 at 11:34
9

First, performance (especially x100 slower) should be enough reason to not use something.

However, you are missing the point. You are now asking "why not use eval in the specific example of evaluating a function in a string?". Well, the answer of that is because you have a function called str2func to specifically do this job faster and more safely. The reason you should not use eval is because in the cases you want to use eval, the logic of your code is flawed.

The only reason to eval is to evaluate an arbitrary input that is not just a function on a string (why would you do that, you already showed that there is an specific function for it). If you know what you are evaluating, then you don't need eval, you can write code for what you expect. Thus, eval is only of use when you accept generic inputs. But generic inputs include rm -rf to delete your whole OS. Or in a less catastrophic case, the code may rewrite over a variable that is important for the rest of the algorithm. It is obvious why you dont want to let your code run arbitrary inputs.

What about dynamic variables? A terrible idea that can be generated using eval. And you may accidentally make this by accepting arbitrary inputs.

But there are more things. eval does make your code unreadable. You have no idea what the code does, until runtime.

I have seen code that does this within its core functions

model.solve_algorithm=eval(['default(',[ class(input3) ],')']);
result=eval(model.solve_algorithm);

What does the code do? What are the options? There is no way to know, unless you run it, and see where it goes. This makes the code obfuscated and hard to read, and certainly hard to maintain. Being explicit in code is something that does benefit a lot in maintainability of code.

TLDR: In any case that you may want to use eval, one of the two is happening:

  1. There is a better, more specific function for what you want to do
  2. You really should not be doing what you are trying to do, code/logic needs restructuring.
Ander Biguri
  • 35,140
  • 11
  • 74
  • 120
  • Thanks for your answer, but I don't see a clear difference with `str2func`. Note that I assign the output of `eval` to a variable, which avoids a lot of tricky code to be executed. – m7913d Sep 14 '17 at 11:37
  • 2
    @m7913d Im going to echo Andras in this. There are enough reasons in my answer, an answer that has in the second paragraph the sentence "you are missing the point". You are, still, missing the point. Re-read the question, as it clearly explains the big picture, not just the cherry picked example that you are trying to defend. – Ander Biguri Sep 14 '17 at 11:38
  • It is not because a (more specific) function exist that it is intrinsic safer. Matlab can create a new function to overcome the bad name of `eval` for a specific situation. I'm, still, missing a concrete example. – m7913d Sep 14 '17 at 11:44
  • 2
    @m7913d You are missing the point. You are looking for a concrete example *within a concrete example* that you gave. A concrete example of unsafety of eval is `eval(whatever_users_says)`. It is easy to cherry pick a case where using eval is safe. Why not use eval in `eval(['a=randi(',input,')'])` ? It will always be safe! But if you just look at this example, you are missing the point – Ander Biguri Sep 14 '17 at 11:47
  • I partially agree with you, but (1) my question was about this specific use case and (2) using the same reasoning, you can say that atomic energy is bad, because you can make a bomb with it. No, because if you use it well, you can create clean energy with it. So, if I use `eval` well, it is equally safe as `str2func` and more convenient to evaluate a function directly. – m7913d Sep 14 '17 at 11:56
  • 3
    Not a great analogy, since it's pretty hard to find an example of "using `eval` well" – Wolfie Sep 14 '17 at 12:01
  • @Wolfie You are right, but the question is not about why `eval` is "well", but why `eval` is worser than the worse function `str2func`. – m7913d Sep 14 '17 at 12:04
8

TL;DR - eval has access to locally defined functions while str2func does not. The "right" function to use among these two depends on the structure of your code.


Without getting into the whole "why eval is bad" discussion, I shall try to focus on the relevant piece of MATLAB documentation that discusses it in the context of your question:

Consider a file with two functions:

function out = q46213509
  f='a^x+exp(b)+sin(c*x)+d';
  fHandle = {eval(['@(x, a, b, c, d) ' f]), str2func(['@(x, a, b, c, d) ' f])};
  out = [fHandle{1}(1,2,3,4,5) fHandle{2}(1,2,3,4,5)];
end

function e = exp(x)
  e = x.^0./factorial(0) - x.^1./factorial(1) + x.^2./factorial(2); % Error is deliberate
end

When you run the above, it results in :

ans =

    8.7432   26.3287

If you're working with classes and you define your own operators this might get out of hand... Let's say somebody decided to add a file to your MATLAB path, and conveniently give it the name of some function you use, or that of an overloadable operator (i.e. mpower.m):

function out = mpower(varargin)
  % This function disregards all inputs and prints info about the calling workspace.
  disp(struct2cell(evalin('caller','whos')));
end

While in some cases, MATLAB protects from redefinition of builtin functions, I bet the scenario above might confuse str2func quite a lot...

Dev-iL
  • 23,742
  • 7
  • 57
  • 99
  • I appreciate your nice, open minded and to the point answer. Thanks! However, it may depends on the user which behaviour is desirable. – m7913d Sep 14 '17 at 13:26
  • @m7913d I'm confused, you've been presented with a "concrete example" of where they differ and which might be preferable and why, but this isn't the answer you were looking for? – Wolfie Sep 14 '17 at 13:36
  • @Wolfie If this is the only difference, i.e. there are no other more critical differences, than this is indeed the answer I'm looking for. Considering the first example, I can not say which answer is clearly preferred. If I, as a user, would by purpose overwrite the default `exp` function and supply `f` to a black box function, I would probably expect the result that `eval` returns. – m7913d Sep 14 '17 at 13:45
  • Thanks again for your answer, but I will accept Wolfie's answer as it is somewhat broader. – m7913d Sep 15 '17 at 07:37
  • @m7913d That's cool, as long as this discussion is put to rest :) – Dev-iL Sep 15 '17 at 09:12
8

I'll put aside the arguments against both methods for a moment, although they are all very valid and a conscientious reader should try to understand them.


I can see two clear differences. For these examples we have to assume you've constructed the input string to the functions in your script, using some variable data, and assume it could be anything (by mistake or otherwise). So we prepare and account for the worst case.

  1. str2func includes some extra checks to make sure what you've passed is a valid function, which might avoid unwanted behaviour. Let's not take the stance of "yeah but you could do it this way to avoid that" and look at an example...

    % Not assigning an output variable
    % STR2FUNC: You've harmlessly assigned ans to some junk function
    str2func('delete test.txt') 
    % EVAL: You've just deleted your super important document
    eval('delete test.txt')     
    
    % Assigning an output variable
    % STR2FUNC: You get a clear warning that this is not a valid function
    f = str2func('delete test.txt') 
    % EVAL: You can a non-descript error "Unexpected MATLAB expression"
    f = eval('delete test.txt')
    
  2. Another difference is the subject of about half the str2func documentation. It concerns variable scopes, and is found under the heading "Examine Differences Between str2func and eval".

    [Intro to the function]
    If you use a character vector representation of an anonymous function, the resulting function handle does not have access to private or local functions.

    [Examine Differences Between str2func and eval]
    When str2func is used with a character vector representing an anonymous function, it does not have access to the local function [...]. The eval function does have access to the local function.


So to conclude, you might have use cases where each function is preferable depending on your desired error handling and variable scoping should never use eval or str2func wherever possible, as stated in the other answers.

Wolfie
  • 27,562
  • 7
  • 28
  • 55
  • Thanks for putting aside the arguments against both methods. I definitely agree with you that you should avoid `eval` and `str2func` whenever possible. I see the difference between both, but - like the answer of Dev-iL - the preferred behaviour depends rather on the user (if you do not take 1 - not assigning an output variable into account). Maybe you were true that the question has only an opinion based answer, because both are almost the same in this situation. – m7913d Sep 14 '17 at 13:42
  • 3
    Yes, the preferred behaviour depends on the user, that's why I didn't say I preferred on function over the other. But that's applicable to almost *any* programming feature. This is simply a comparison of the different behaviours of two very similar functions. The opinion here is "both are bad", the facts are everything other than my last sentence. – Wolfie Sep 14 '17 at 13:46
  • That's why I did not understand why Luis Mendo commented to my previous question: _please mention in your answer how `eval` is undesirable for reasons more fundamental than performance_. I interpreted this comment that there are more fundamental reasons to use `str2func` instead of `eval`. – m7913d Sep 14 '17 at 13:52
  • 3
    I think what was actually implied by Luis was "please mention in your answer how `eval` is undesirable for reasons more fundamental than performance, as is `str2func` even if it's quicker". Although I cannot speak for him, it's an easy go-to dismissing `eval`, `str2func` is less common so not as quickly dismissed (although it can be for most the same reasons, but as written above there are *some* more protections) – Wolfie Sep 14 '17 at 13:55
  • 2
    @m7913d Yes, I meant exactly what Wolfie said in the comment above: don't use `eval` because it can cause problems. I wasn't really comparing with `str2func`. I see now how my comment may have confused you (sorry!), because your question was about comparing both – Luis Mendo Sep 14 '17 at 21:32
  • Anyway, it _is_ true that `str2func` is slightly less dangerous than `eval`, as this answer and others have shown – Luis Mendo Sep 14 '17 at 22:08