51

Most modern languages (which are somehow interpreted) have some kind of eval function. Such a function executes arbitrary language code, most of the time passed as the main argument as a string (different languages may add more features to the eval function).

I understand users should not be allowed to execute this function (edit i.e. take directly or indirectly arbitrary input from an arbitrary user to be passed to eval), especially with server-side software, since they could force the process to execute malicious code. In that way, tutorials and communities tell us to not use eval. However, there are many times where eval is useful and used:

  • Custom access rules to software elements (IIRC OpenERP has an object ir.rule which can use dynamic python code).
  • Custom calculations and/or criteria (OpenERP has fields like that to allow custom code calculations).
  • OpenERP report parsers (yes I know I'm freaking you out with OpenERP stuff... but it is the main example I have).
  • Coding spell effects in some RPG games.

So they have a good use, as long as they are used properly. The main advantage is that the feature allows admins to write custom code without having to create more files and include them (although most frameworks using eval features have also a way to specify a file, module, package, ... to read from).

However, eval is evil in the popular culture. Stuff like breaking into your system comes to mind.

However, there are other functions which could be harmful if somehow accessed by users: unlink, read, write (file semantics), memory allocation and pointer arithmetic, database model access (even if not considering SQL-injectable cases).

So, basically, most of the time when any code is not written properly or not watched properly (resources, users, environments, ...), the code is evil and can lead even to economic impact.

But there's something special with eval functions (regardless of the language).

Question: Is there any historical fact for this fear becoming part of the popular culture, instead of giving the same attention to the other possibly dangerous features?

14
  • 11
    I disagree that this is too broad, and as evidence I present the four answers that are of a reasonable length.
    – user22815
    Commented Mar 1, 2016 at 17:26
  • 12
    They voted to close as too broad? (I can't see close votes yet in this community) That close reason is becoming a wildcard reason with no meaning at all lately. Specially when the point I ask is quite clear with examples and a specific point to ask. I will ask this topic in Meta... Commented Mar 1, 2016 at 17:29
  • 13
    Why it become part of popular culture, with more attention than other dangerous features? Because the alliteration eval - evil is easy to remember
    – Bergi
    Commented Mar 1, 2016 at 18:47
  • 5
    Memory allocation and pointer arithmetic are seen as evil, by many.
    – user20574
    Commented Mar 1, 2016 at 20:05
  • 5
    It should be noted that OpenERP (now Odoo) doesn't just call eval, it has an internal function called safe_eval that is prepares the environment to prevent the code from doing dangerous things. Bugs have been found, though, since Python is a quite flexible language, and therefore hard to control. Commented Mar 1, 2016 at 22:40

13 Answers 13

76

An eval function by itself is not evil, and there is a subtle point that I do not believe you are making:

Allowing a program to execute arbitrary user input is bad

I have written code that used an eval type of function and it was secure: the program and parameters were hard-coded. Sometimes, there is no language or library feature to do what the program needs and running a shell command is the short path. "I have to finish coding this in a few hours, but writing Java/.NET/PHP/whatever code will take two days. Or I can eval it in five minutes."

Once you allow users to execute anything they want, even if locked down by user privilege or behind a "secure" screen, you create attack vectors. Every week, some random CMS, blogging software, etc. has a security hole patched where an attacker can exploit a hole like this. You are relying on the entire software stack to protect access to a function that can be used to rm -rf / or something else catastrophic (note: that command is unlikely to succeed, but will fail after causing a bit of damage).

Is there any historical fact for this fear becoming part of the popular culture, instead of putting the same attention to the other possibly dangerous features?

Yes, there is a historical precedent. Due to the numerous bugs that have been fixed over the years in various software that allow remote attackers to execute arbitrary code, the idea of eval has mostly fallen out of favor. Modern languages and libraries have rich sets of functionality that make eval less important, and this is no accident. It both makes functions easier to use and reduces the risk of an exploit.

There has been much attention paid to many potentially insecure features in popular languages. Whether one receives more attention is primarily a matter of opinion, but the eval features certainly have a provable security problem that is easy to understand. For one, they allow executing operating system commands including shell built-ins and external programs that are standard (e.g. rm or del). Two, combined with other exploits, an attacker may be able to upload their own executable or shell script then execute it via your software, opening the door for almost anything to happen (none of it good).

This is a difficult problem. Software is complex, and a software stack (e.g. LAMP) is multiple pieces of software that interact with each other in complex ways. Be careful how you use language features such as this, and never allow users to execute arbitrary commands.

9
  • 2
    You are the one :). Although if there's a good well-known example which contributed to this culture, I would like to see it in the answer. Commented Mar 1, 2016 at 17:27
  • 5
    I am not aware of any single example, I believe this is more "death by a thousand cuts" with many smaller examples of exploits being used and patched. I am not exaggerating when I say that some remote exploit is patched on a weekly basis in some piece of software.
    – user22815
    Commented Mar 1, 2016 at 17:33
  • 1
    My IDE is a program that allows me - it's user - to execute arbitrary input. I find it useful rather than bad.
    – Den
    Commented Mar 2, 2016 at 11:59
  • 3
    @Den that would be an exception. Being an IDE, I am sure you could cause mayhem without that ability. Just write it in your source code. Also, you already have full access to the computer on which it runs. The implication here is that an eval may elevate privileges. which is not an issue if they are already elevated.
    – user22815
    Commented Mar 2, 2016 at 16:50
  • 3
    @Den: It's what Raymond Chen summarized as "the other side of the airlock" . You can already run rm -rf /, there is no need to do that in a complicated way through an IDE. The problem with eval is that it opens up that ability to a lot of actors who shouldn't have that ability.
    – MSalters
    Commented Mar 3, 2016 at 15:15
38

Part of it is simply that nuance is hard. It easy to say thing like never use goto, public fields, string interpolation for sql queries, or eval. These statements shouldn't really be understood as saying there is never, under any circumstances, a reason to use them. But avoiding them as a general rule of thumb is good idea.

Eval is heavily discouraged because it combines several common issues.

Firstly, it is susceptible to injection attacks. Here it is like SQL injection in that when user controlled data is inserted into the code, its easily to accidentally allow arbitrary code to be inserted.

Secondly, beginners tend to use eval to get around badly structured code. A beginner coder might write code that looks like:

x0 = "hello"
x1 = "world"
x2 = "how"
x3 = "are"
x4 = "you?"
for index in range(5):
   print eval("x" + index)

This works, but is really the wrong way to solve this problem. Obviously, using a list would way better.

Thirdly, eval is typically inefficient. A lot of effort is spent speeding up our programming language implementations. But eval is difficult to speed up and using it will typically have detrimental effects on your performance.

So, eval is not evil. We might say that eval is evil, because well, its a catchy way to put it. Any beginner coder should strictly stay away from eval because whatever they are wanting to do, eval is almost certainly the wrong solution. For certain advanced use cases, eval makes sense, and you should use it, but obviously be careful of the pitfalls.

5
  • 13
    Your second case is very important. In languages that have eval but are also compiled, evaluating a string to produce a variable reference is non-trivial. E.g., if var x = 3; print(x) is compiled then there doesn't need to be any run-time knowledge that the source used the name "x". In order for var x = 3; print(eval("x")) to work, that mapping needs to be recorded. This is a real issue: in Common Lisp (let ((x 3)) (print (eval 'x))) will throw an unbound variable exception because the lexical variable x doesn't have any connection with the name after the code is compiled. Commented Mar 1, 2016 at 19:36
  • @JohnuaTaylor You mean third reason, not second?
    – user20574
    Commented Mar 2, 2016 at 7:45
  • 1
    @immibis, I think he's referring to the code in the second reason. But you are correct, its really relating to the third reason: performance. Commented Mar 2, 2016 at 17:12
  • Saw this question, didn't you? ;)
    – jpmc26
    Commented Mar 3, 2016 at 1:29
  • @jpmc26, no. But I've seen plenty like it. Commented Mar 3, 2016 at 1:34
20

What it boils down to is that "arbitrary code execution" is tech-talk for "able to do anything." If someone is able to exploit arbitrary code execution in your code, this is literally the worst security vulnerability possible, because it means they are able to do anything that is possible for your system to do.

"Other possibly harmful bugs" may well have limits, which means that they are, by definition, capable of less harm than an arbitrary code execution being exploited.

6
  • 2
    For the sake of argument, misuse of pointers also leads to arbitrary code execution, yet pointers are frowned upon much less than eval is. Commented Mar 2, 2016 at 8:10
  • 12
    @DmitryGrigoryev Are they really? Outside of C++, pointers are generally taken to be extremely dangerous and avoided. C# supports pointers, for example, but it tends to be the last thing you try after you exhaust all other options (usually, for performance reasons on data manipulation). Most modern languages have no support for pointers. Even C++ itself is moving towards "safer" pointers that try to alleviate the problems with arbitrary pointers (e.g. using true lists/arrays instead of just pointers, using safe_ptr, reference passing...).
    – Luaan
    Commented Mar 2, 2016 at 8:48
  • 2
    @DmitryGrigoryev: Can you provide a reference for anybody saying pointers are less dangerous than eval?
    – JacquesB
    Commented Mar 2, 2016 at 13:58
  • 2
    @JacquesB here you go :) Commented Mar 2, 2016 at 14:02
  • 1
    @JacquesB, yes; it was intended as a joke (I expected the smile would make it clear enough). The OP made that statement, not me, so you should ask him for proof. Commented Mar 3, 2016 at 12:44
15

There's a practical and a theoretical reason.

The practical reason is that we observe it frequently causes problems. It's rare that eval results in a good solution, and it often results in bad solution where you'd have got a better one in the end if you'd pretended eval didn't exist and approached the problem differently. So the simplified advice is to ignore it, and if you come up with a case where you want to ignore the simplified advice, well, let's hope you've thought it through sufficiently to understand why the simplified advice isn't applicable and the common pitfalls won't affect you.

The more theoretical reason is that if it's hard to write good code, it's even harder to write code that writes good code. Whether you're using eval, or generating SQL statements by sticking together strings, or writing a JIT compiler, what you're attempting is often harder than you expect. The potential for malicious code injection is one big part of the problem, but aside from that it is in general harder to know your code's correct if your code doesn't even exist until runtime. So the simplified advice is to keep things easier for yourself: "use parameterized SQL queries", "don't use eval".

Taking your spell effects example: it's one thing to build a Lua (or whatever) compiler or interpreter into your game in order to allow game designers an easier language than C++ (or whatever) to describe spell effects. Most of the "problems of eval" don't apply if all you're doing is evaluating code that has been written and tested and included in the game or in DLC or what-have-you. That's just mixing languages. The big problems hit you when you try to generate Lua (or C++, or SQL, or shell commands, or whatever) on the fly, and mess it up.

1
  • 1
    Of course, runtime code generation can be done correctly, and eval can be useful. E.g. I frequently use eval for metaprogramming/macro-like stuff, especially when I want more performance than a “cleaner” OOP or functional solution could provide. The resulting code is often better and simpler because has less boilerplate. However, being aware of various problems regarding sandboxing, escaping mechanisms, code injection, scoping rules, error reporting, optimization etc. requires a nontrivial level of proficiency in the language, and eval is positively dangerous without that knowledge.
    – amon
    Commented Mar 1, 2016 at 18:06
11

No, there is no obvious historical fact.

The evils of eval are plain to see from the beginning. Other features are mildly dangerous. People can delete data. People can see data they shouldn't. People can write data they shouldn't. And they can only do most of those things if you somehow screw up and not validate user input.

With eval, they can hack the pentagon and make it look like you did it. They can inspect your keystrokes to get your passwords. Assuming a Turing complete language, they can literally do anything your computer is capable of doing.

And you can't validate the input. It's an arbitrary free form string. The only way to validate it would be to build a parser and code analysis engine for the language in question. Best of luck with that.

10
  • 1
    ... or have the input coming from a trusted source, such as the spell definition files for a game.
    – user20574
    Commented Mar 2, 2016 at 7:46
  • 3
    @immibis but if the input is predefined and tested safe why use eval when it could be included in the source of the system?
    – Jules
    Commented Mar 2, 2016 at 9:11
  • 3
    @immibis - because nobody ever hacks game files...
    – Telastyn
    Commented Mar 2, 2016 at 12:30
  • 2
    ...that's not what "Turing complete" means (Turing completeness is one step away from irrelevant to real-world computing). Commented Mar 2, 2016 at 21:02
  • 1
    @Telastyn: Which a Javascript program can't do. Or even a C++ program. Javascript runs inside a sandbox (browser) which is itself in another sandbox (ring 3 in x86 speak). The interrupt vector is outside that sandbox, under OS control. And that outer sandbox, ring 3, is CPU-enforced.
    – MSalters
    Commented Mar 3, 2016 at 15:29
6

I think it boils down to the following aspects:

  • Necessity
  • (Guarded) Usage
  • Access
  • Verifiability
  • Multi-stage attacks

Necessity

Hi there, I've written this extremely cool image editing tool (available for $ 0.02). After you have opened the image, you can pass a multitude of filters over your image. You can even script some yourself using Python (the program I've written the application in). I'll just use eval on your input, trusting you to be a respectable user.

(later)

Thanks for buying it. As you can see it functions exactly as I promised. Oh, You want to open an image? No, you can't. I won't use the read method as it is somewhat insecure. Saving? No, I won't use write.

What I'm trying to say is: You need read/write for almost the basic tools. Same for storing the high scores of your oh-so-awesome game.

Without read/write, your image editor is useless. Without eval? I'll write you a custom plugin for that!

(Guarded) Usage

A lot of methods can potentially be dangerous. Like, for instance the read and write. A common example is a web service allowing you to read images from a specific directory by specifying the name. However, the 'name' can in fact be any valid (relative) path on the system, allowing you to read all the files that the web service has access to, not just the images. Abusing this simple example is called 'path traversal'. If your app allows path traversal, it's bad. A read without defending against for path traversal can wel be called evil.

However, in other cases, the string for read is fully under the programmers control (maybe hardcoded?). In that case, it is hardly evil to use read.

Access

Now, another simple example, using eval.

Somewhere in your web-app, you want some dynamic content. You're going to allow the administrators to enter some code which is executable. Seeing as the admins are trusted users, this can theoretically be ok. Just make sure to not execute code submitted by non-admins, and you're fine.

(That is, until you fired that nice admin but forgot to revoke his access. Now your web-app is trashed).

Verifiability.

Another aspect that's important, I think, is how easy it is to verify user input.

Using user input in a read call? Just make (very) sure that the input for the read call does not contain anything malicious. Normalise the path, and verify the file that is opened is in your media directory. Now that's safe.

User input on a write call? Same!

SQL injection? Just escape it, or use parametrised queries and you're safe.

Eval? How are you going to verify the input that's used for the eval call? You can work very hard, but it's really really hard (if not impossible) to make it work securely.

Multi-stage attacks

Now, every time you use user input, you need to weigh the benefits of using it, against the dangers. Protect its usage as much as you can.

Consider again the evalable stuff in admin example. I told you that was sort-of ok.

Now, consider that there's actually a place in your web-app where you forgot to escape user-content (HTML, XSS). That's a lesser offence than user-accessible eval. But, using the unescaped user-content, a user can take over the web-browser of an admin, and add an evalable blob through the admins session, allowing again full system access.

(Same multi-stage attack can be done with SQL injection instead of XSS, or some arbitrary file writes replacing executable code instead of using eval)

4
  • 2
    "You can work very hard, but it's really really hard (if not impossible) to make it work securely." – It is impossible. Simple proof: instead of trying to figure out whether or not the code the user provided is "secure", just try to figure out something much, much simpler, and see how hard that is already: does the code provided by the user halt? Commented Mar 1, 2016 at 22:25
  • 2
    @JörgWMittag: give me a program written in Coq and I'll prove it. Commented Mar 1, 2016 at 22:53
  • 1
    @JörgWMittag: Agreed. Depending on the language, and the scope of the user input. It could as well be eval("hard coded string" + user_input_which_should_be_alphanumeric + "remainder"). Verifying that the input is alphanumeric is possible. Also, 'does it halt' is orthogonal to 'does it modify/access state it should not touch' and 'does it cal methods it should not call'. Commented Mar 2, 2016 at 8:27
  • 4
    @JörgWMittag It's impossible to prove that a given program won't do something, but it's not impossible to conservatively define a restricted set of programs for which you can prove it, and to enforce that input programs are members of that set.
    – user20574
    Commented Mar 3, 2016 at 2:20
3

In order for this feature to work at all, it means I need to keep a reflection layer around that enables full access to the entire program's internal state.

For interpreted languages, I can simply use the interpreter state, which is easy, but in combination with JIT compilers it still significantly increases complexity.

Without eval, the JIT compiler can often prove that a thread's local data is not accessed from any other code, so it is perfectly acceptable to reorder accesses, omit locks and cache often-used data for longer times. When another thread executes an eval statement, it may be necessary to synchronize the running JIT compiled code against that, so suddenly the JIT generated code needs a fallback mechanism that returns to unoptimized execution within a sensible timeframe.

This kind of code tends to have lots of subtle, hard to reproduce bugs, and at the same time it also places a limit on optimization in the JIT compiler.

For compiled languages, the tradeoff is even worse: Most optimizations are forbidden, and I need to keep extensive symbol information and an interpreter around, so the additional flexibility is generally not worth it -- it is often easier to define an interface to some internal structures, e.g. by giving a scripting view and controller concurrent access to the program's model.

1
  • "In order for this feature to work at all, it means I need to keep a reflection layer around that enables full access to the entire program's internal state" - no, just to the parts you want to affect. In the case of OpenERP/Odoo, the code being evaled only has access to a very limited number of variables and functions it can call, and they're all thread-local. Commented Mar 1, 2016 at 22:59
1

I reject the premise that eval is considered more evil than pointer arithmetic or more dangerous than direct memory and file system access. I don't know of any sensible developer who would believe that. Furthermore, languages supporting pointer arithmetic/direct memory access typically does not support eval and vice-versa, so I'm note sure how often such a comparison would even be relevant.

But eval might be a more well known vulnerability, for the simple reason that it is supported by JavaScript. JavaScript is a sandboxed language without direct memory or file system access, so it simply does not have these vulnerabilities barring weaknesses in the language implementation itself. Eval is therefore one of the most dangerous features of the language, since it open the possibility of arbitrary code execution. I believe many more developers develop in JavaScript than in C/C++, so eval is simply more important to be aware of than buffer overflows for the majority of developers.

0

No serious programmer would consider Eval to be "Evil". It's simply a programming tool, like any other. The fear (if there is a fear) of this function has nothing to do with popular culture. It's simply a dangerous command which is often misused, and can introduce serious security holes, and degrade performance. From my own experience I would say that it's rare to encounter a programming problem which cannot be solved more safely and efficiently by some other means. The programmers who are most likely to use eval, are those who are probably least qualified to do so safely.

Having said that, there are languages where the use of eval is appropriate. Perl comes to mind. However I personally find it's very rarely needed in other, more modern languages, which natively support structured exception handling.

2
  • this doesn't even attempt to address the question asked, "Is there any historical fact for this fear becoming part of the popular culture". See How to Answer
    – gnat
    Commented Mar 3, 2016 at 10:51
  • The question, in my opinion, is based on a false premise, so I answered it as such. Commented Mar 3, 2016 at 12:53
0

I think you cover it quite well in the following part of your question (emphasis mine):

they have a good use, as long as they are used properly

For the 95% who might use it properly it's all well and good; but there will always be people who don't use it properly. Some of those will be down to inexperience and lack of ability, the rest will be malicious .

There will always be people who want to push boundaries and find security holes - some for good, some for bad.

As for the historical fact aspect of it, eval type functions essentially allow Arbitrary code execution which has previously been exploited in popular web CMS, Joomla!. With Joomla! powering over 2.5 million sites worldwide, that's a lot of potential damage not only to visitors to those sites, but potentially to the infrastructure it's hosted on and also the reputation of the sites/companies which have been exploited.

The Joomla! example might be a simple one, but it's a one which has been recorded.

0

There are some good reasons to discourage the use of eval (although some are specific to certain languages).

  • The environment (lexically and dynamically) used by eval is frequently surprising (that is, you think eval(something here) should do one thing, but it does another, possibly triggering exceptions)
  • There is frequently a better way of accomplishing the same thing (concatenation of constructed lexical closures is sometimes a better solution, but that may well be Common Lisp-specific)
  • It is far too easy to end up with unsafe data being evaluated (although this can mostly be guarded against).

I would, personally, not go as far as saying that it's evil, but I will always challenge the use of eval in a code review, with wordings along the line of "have you considered some code here?" (if I have time to make at least a tentative replacement), or "are you sure an eval really is the best solution here?" (if I don't).

1
  • this doesn't even attempt to address the question asked, "Is there any historical fact for this fear becoming part of the popular culture". See How to Answer
    – gnat
    Commented Mar 3, 2016 at 10:50
0

In Minsky's Society of Mind, Chapter 6.4, he says

There is one way for a mind to watch itself and still keep track of what's happening. Divide the brain into two parts, A and B. Connect the A-brain's inputs and outputs to the real world - so it can sense what happens there. But don't connect the B-brain to the outer world at all; instead connect it so that the A-brain is the B-brain's world!

When one program (B) writes another program (A) it is functioning as a meta-program. B's subject matter is program A.

There is a C program. That's the one in your head that writes program B.

The danger is there can be somebody (C') with evil intent, who can interfere in this process, so you get things like SQL-injection.

The challenge is to make software smarter without also making it more dangerous.

1
  • Two upvotes, and two downvotes. Looks like maybe this hits a nerve. Commented Mar 3, 2016 at 20:01
0

You have a lot of good answers here and the main reason is clearly that arbitrary code execution is bad mmmkay but I will add one other factor that others have only touched on the edge of:

It is really hard to troubleshoot code that is being evaluated out of a text string. Your normal debugging tools are pretty much straight out of the window and you are reduced to very old-school tracing or echoing. Obviously that doesn't matter so much if you have a fragment in a one-liner which you want to eval but as an experienced programmer you can probably find a better solution for that, whereas larger-scale code generation may be somewhere that an evaluation function becomes a potentially useful ally.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.