41

Suppose I'm reviewing code that job applicants send to prove their skills. Clearly I don't want to run executables they send. Not so clearly I'd rather not run the result of compilation of their code (just for example, Java allows to hide runnable code in comments).

What about compiling their code? I want compiler warnings if any but what if their code contains some clever character sequences which exploit my compiler and my compiler compromises my machine?

When I Google for "compiler vulnerabilities" the hits I get are all about compiler optimizations and code emission and whether emitted code is as secure as original source code was intended to be.

Are compilers typically validated to ensure they won't compromise the user machine when compiling some clever piece of code? How safe is it to compile a piece of code from a stranger?

22
  • 40
    Just use a virtual machine... Jun 23, 2015 at 11:29
  • 14
    If you are actually reviewing the code, then it should be quite hard to get something as hard as "pwning the user machine" through without being noticed, right? Jun 23, 2015 at 11:31
  • 17
    So how do you judge their skills? I ask this because I've often reviewed code sent to us by job applicants, but I've always just read it, never felt any need to execute it. Jun 23, 2015 at 11:39
  • 9
    Java does allow hiding of runnable code in comments. Not all Java IDEs perform unicode conversions when doing syntax highlighting, which is not remotely the same thing. Jun 23, 2015 at 12:55
  • 69
    Don't even read the code. They could exploit a vulnerabilty in your brain.
    – Henrik
    Jun 23, 2015 at 13:30

13 Answers 13

35

It depends.

This piece of makefile could delete your home directory :

all:
    rm -rf ~

So, if you need to use a tool (like cmake or makefile system), then it is not safe. It just depends how malicious the coder is.

On the other side, compilers are programmed by people, therefore they got bugs. So, maybe it could be possible that someone found a way to execute malicious code during the compilation.

As suggested in the comments, if you want to be sure that no funny things is being done to your machine, use a virtual machine.

10
  • 32
    "use a virtual machine" -- and if you're really paranoid, bear in mind that by exploiting multiple flaws, malware can potentially climb up out of your VM and strangle you (venom.crowdstrike.com) Jun 23, 2015 at 13:18
  • 23
    @SteveJessop yeah, better use nested VMs ;) Chances are the coder doesn't realize that having broken out of one VM, he's still inside the other.
    – Ruslan
    Jun 23, 2015 at 15:20
  • 3
    Or just use an old laptop to test the code on. As long as it has no network capabilities you'll always be sure the malware won't jump out. Unless the software bugs magically materialize into real bugs of-course.
    – Mast
    Jun 23, 2015 at 17:11
  • 5
    @Ruslan: unfortunately most hackers have seen Inception. Jun 23, 2015 at 17:20
  • 2
    @Ruslan This is literally the tab I have opened before this page: matrix.wikia.com/wiki/Matrix_in_a_Matrix_theory which I was reading because of an unrelated question on the SciFi SE site.
    – armani
    Jun 23, 2015 at 19:26
23

I am pretty sure somewhere in the business there are some clever guys who have already created such a hack for a specific language and compiler version. My favorite place to look for something like this would probably be the International Obfuscated C contest - (do not know if there is something comparable for Java). However, in reality, how high do you consider the risk, assumed that

  • the applicant makes a plausible impression on you he really wants the job at your company (and not a lawsuit)

  • the guy does not know how much reviewing is done at yours

  • he / she does not know which exact compiler version you are using

  • he / she does not know if you use a virtual environment or an online compiler, just to be safe

  • you do not accept programs which are too large to be effectively reviewed

  • you do not compile anything which looks suspicious to you

  • there are not many people in the world who actually know how to technically accomplish such a task (and googling alone won't give you a "quick ref" or tutorial on this, as you have already found out by yourself).

So though compiling is not "totally safe" in theory, IMHO in reality the risk is extremely low that your "compiler gets pwned".

3
  • 15
    Obfuscated is good. Underhanded is better. underhanded-c.org
    – user40980
    Jun 23, 2015 at 13:22
  • 11
    "the applicant really wants a job at your company (and not a lawsuit)" It's not clear that a stranger who sends an application really wants the job.
    – Christian
    Jun 23, 2015 at 17:09
  • @Christian: obviously. But I guess the OP would not invest any time into the review of an applicant's code if the latter did not come up with at least a plausible appearance concerning his job request. And also a stranger does probably not want to be sued. My point is: each of the above points on its own may be circumvented, but all together? That is a pretty low risk.
    – Doc Brown
    Jun 24, 2015 at 5:12
14

We have to distinguish several cases:

  1. A bug in the compiler. Like every complex program, a compiler might have bugs, and one of those bugs might be exploitable.
  2. A Trojan Horse. The attacker may get you to execute some arbitrary code as part of the compilation process. A Makefile, a build.xml, a configure shell script etc. Technically, this is not due to compiling the attacker's code but rather setting up the compile environment.
  3. Languages allowing arbitrary code to run at compile time. Scala's macro language is Scala, Common Lisp's macro language is Common Lisp, Template Haskell's macro language is Haskell. Scala also has compiler plugins, which are again arbitrary Scala code that runs at compile time. F# has type providers.
  4. Languages which allow Turing-computation at compile time. Scala's and Haskell's type systems are Turing-complete, as are C++'s Templates. You can get the compiler to perform arbitrary Turing-computation at compile time, including but not limited to infinite loops. Note that Turing-complete only means that you can compute every Turing-computable function, it does not mean that you can access the filesystem or something like that. But, you can make a program that will take infinitely long to compile.
  5. Very long compile times. For example, C#'s rules for overload resolution are so complex that you can encode any 3-SAT problem as C# overload resolution. 3-SAT is, of course, famously NP-complete. In other words, according to our current knowledge, it is impossible to find an efficient algorithm for overload resolution in C#. You can't make compilation take infinitely long, but it doesn't take a big program to make compilation take longer than the lifetime of the universe, which is practically the same thing.

#4. and #5. will at the most result in a denial of service. Practically, C++ and Scala compilers limit the amount of recursion you can do, so that it is not actually possible to write an infinite loop. In Scala, that's just an implementation constraint, but in C++, it's explicitly allowed by the spec, I believe.

#2. is technically out of scope of the question because the question was about compiling code not running it (OTOH, there's the deep philosophical question: if type-checking a Haskell program can perform arbitrary Turing-computation, is that compilation or running a program?)

#1. is unlikely. On the one hand, production compilers are very complex, so the likelihood of bugs is high. On the other hand, they are rigorously tested, after all, handling ill-formed input gracefully is part of the job description of a compiler. Even if they are not tested, they will be bombarded with ill-formed code anyway … just look at some StackOverflow questions for examples of what junk people throw at their compilers!

This leaves us with 3. Some compilers may limit the kind of access the compile time code has to the system, but for some of the use cases, having full access is unavoidable. The purpose of F#'s type providers, for example, is to "fake" synthetic types for data whose type system doesn't match F#'s, so that you can interact with, say, a web service that has a WSDL schema in a strongly-typed fashion. However, in order to do this, the type provider needs to have access to the WSDL schema resource either on the filesystem or on the web, so it needs to have filesystem and network access.

So, is it safe? Technically, no. Is it risky? Not really.

2
  • 1
    C++ indeed limits the instantiation depth of templates to some implementation-dependent value. This used to be a few dozen; modern implementations have raised the limit to several hundred. Still, it's completely trivial to double the number of templates instantiations per level, so 100 levels would require the compiler to instantiate 2^100 templates. That's till just a DoS attack.
    – MSalters
    Jun 24, 2015 at 10:20
  • I like the categories presented by this answer - but in the end it seems the bottom line is really "usually fine, but for certain languages, depends whether you trust a random developer not to exert an incredible amount of work to compromise your system." So...maybe don't compile foreign code on e.g. nuclear control computers, haha. Maybe don't put foreign files on nuclear control computers, either, haha.
    – Erhannis
    Jan 19, 2021 at 16:00
3

There shouldn't be any risk just compiling the code. In theory there could be a bug in the compiler that a clever hacker could take advantage of but is sounds extremely unlikely.

Be aware that building may be unsafe. For example in C# 'build event' allows you to specify arbitrary command lines to execute before and after building, which is obviously dangerous, and a lot easier to exploit than say buffer overflows in the compiler code.

2
  • 3
    Scala, Template Haskell, almost all Lisps can execute arbitrary code at compile time. All languages with a Turing-complete type system (Scala, Haskell) can perform arbitrary Turing-computation at compile time including but not limited to an infinite loop. C++'s Template system is Turing-complete, again, allowing you to perform arbitrary compution including infinite loops at compile time. C#'s overload resolution is equivalent to 3-SAT, therefore NP-complete, which is not Turing-complete but will still allow you to hang the compiler for the lifetime of the universe if you want. Jun 23, 2015 at 14:42
  • 3
    A Turing-complete type system does not allow you to pwn the computer. At worst it will make the compiler hang if you exploit it. But yeah if you have a languages where the compilation step may execute arbitrary code then obviously you shouldn't compile untrusted code.
    – JacquesB
    Jun 23, 2015 at 15:22
3

Instead of speculating, I actually bothered to do some research on this topic before answering, going to the most authoritative resource I could think of (CVE Details). This comprehensive list of publicly disclosed security exploits is probably the best that one could do to assess the threat levels of various types of software.

I didn't take time to read all of the available material, of course, but I selected a few "primary" compilers, IDEs, and text editors to come up with a sample threat assessment. If you're serious about running any software at all, you should at least see what threats are out there. Also note that older software is generally buggier than newer software, so running the latest of whatever you're running is ideal.

First, we can take a look at various text editors. It seems the best editors are the most simple. Vi if you're using a Linux shell, or Notepad if you're in Windows. Something with no formatting capabilities, no parsing, just straight-forward viewing of data and automatic termination of parsing if a single character is outside of the current encoding scheme. Even Notepad++ has had a handful of vulnerabilities. Avoid anything complex when viewing untrusted files.

Second, we can look at IDEs. If you choose to open the file in an IDE, you should be aware that some IDEs have had reported bugs. Apparently Visual Studio has had exploits available through the extensions mechanism, so opening a solution might be problematic. Avoiding IDEs avoids an entire class of problems between you and the untrusted code. Sticking with VI seems a lot safer.

Third, we can look at actual compilers. I browsed a few, including Adobe, Microsoft, Java, and GNU's C/C++, and found that generally speaking, compiling code (and even building, assuming no custom make-file) is relatively safe, but each of those compilers do or did have security exploits that could arise from actually running the compiled binaries. In other words, they couldn't take your system over simply by compiling, but they could by running code.

So, in conclusion, assuming the delivery method didn't already hijack your system (e.g. your email client got hacked, or the USB drive it came on was infected...), reading the source code and compiling the source code is probably safe. By researching your specific software, you could make it even safer by, say, validating the file is in the correct code page, etc. Running the code should only be done on hardware you simply don't care about. Not a VM, but an entire physically different computer with no network access and no sensitive files or external devices. Even if you think you understand the code, simple research shows that even compilers have bugs which might allow a hidden buffer overflow exploit to sneak up from behind and execute arbitrary code, but only if you choose to run or debug the program. Actual compilation should be safe.

2

Well, I would start with "reviewing their code". Why is there a need to actually run the code?

Apart from that, there are a lot of online compilers where you can just put in the code and compile and/or run it. You can make that a requirement: it compiles in this and that online compiler.

Here's an example of a page with online compilers: Online compilers

Code for review for a job interview shouldn't be so large anyhow as for you not to understand what's going on.

1
  • 3
    "Why is there a need to actually run the code?". To find out whether it's any good, of course, a review only tells you that its failings are subtle :-). "Beware of bugs in the above code; I have only proved it correct, not tried it." - Knuth Jun 23, 2015 at 13:54
2

Are compilers typically validated to ensure they won't pwn the user machine when compiling some clever piece of code?

In general they're too complex and often written using languages in which it's not practical to prove this property.

Possibly not with this specific intent, but the notion of fuzz testing compilers is at least known (LLVM can now fuzz-test itself). Tests intended to catch input that crashes the compiler due to compiler bugs will tend to also turn up exploitable flaws.

Naturally you'd have to look into whether the specific compiler you're interested in is tested or fuzz-tested to find potential crashes, and whether the bugs so found are actually fixed. Rule of thumb is that if there are crashes any worse than uncaught out of memory exceptions, then without investigating the details further you have to consider a serious possibility they could be leveraged into exploits.

How safe is it to compile a piece of code from a stranger?

Unfortunately, how long is a piece of string. In principle the email might exploit your mail client, or the source code might exploit your text editor or cppcheck, before it even reaches your compiler. Sebastian's suggestion in comments to use an online compiler is a pretty good one, but of course the code has to be in a form the compiler will accept.

Any language or compiler with facilities for compile-time execution of general code is of course highly suspect. C++ templates are functionally complete but don't have (intended) access to the system, so they're relatively low-risk. BЈовић mentions make as extremely high-risk (since it's executing the stranger's code, it's just that the code happens to be written in the make language, not in C++). If the compiler will run system then you're in the same boat. I used to work with an assembler that, if I remember rightly, could do arbitrary compile-time code execution. It was intended for computing look-up tables, but I don't think anything prevented you making system calls.

In practice, if the code looks OK to me and I think I understand it, then I'd consider it extremely low risk to compile it, far lower risk than say "browsing the internet with a locked-down browser". I do riskier things routinely on my general-purpose machine, but many of them I wouldn't do e.g. inside a virus lab or on a critical server. If the code is funny-looking or evidently obfuscated then I might not risk compiling it because, aside from the risk it could contain an exploit hidden in the unreadable garbage, it's rubbish code. Underhanded code is difficult but possible. Underhanded code that pwns the machine via a compiler exploit needs to contain a non-trivial executable payload, so is extremely difficult.

If you want to look into this further, try asking the folks who host online compilers. If it hasn't been done to them then (barring you coming to the attention of the NSA or equivalent) you can reasonably assume it won't be done to you. They put some effort into running their compiler in a proper sandbox, which might be more effort than you're willing to go to, but they might at least be able to tell you how often that sandbox saves them trouble.

2
  • Due to work by Professor John Regehr at University of Utah, clang and gcc have gone through rather intensive fuzz testing, hammering out hundreds of defects that could cause the compiler to crash or even produce code that behaved differently from other compilers. The thing to look for would be bugs that are still open, and whether they constitute enough of a threat. Jun 23, 2015 at 16:03
  • @Novelocrat: agreed, and thanks for the specific info. My fear is that the compiler dev team might rate bugs as low-priority because "nobody would ever write that code", and they haven't been fixed yet, whereas once you're thinking of the compiler as an attack surface you would consider them critical. Mind you, hopefully pride would ensure that a compiler-writer wouldn't let something so embarrassing as a buffer write overflow stand ;-) Jun 23, 2015 at 17:25
1

Although this is generally a concern, I think the issue is non-existent due to the setup.

The applicant sent you some source code. How or why did that happen?

Well obviously there are only three possibilities:

  1. You gave the applicant an assignment to solve a particular (well-defined) problem to assess his skills.
  2. The applicant wants to show off something cool he wrote.
  3. The applicant is a jerk or a spy or an otherwise malicious person and not actually interested in being hired. All he hopes for is you being stupid enough to run his code.

About 2) and 3)

The main risk is distinguishing between 2) and 3). Chances are high that if whatever he wrote is worth looking at, it's something that you can either get the source code for online (from a "neutral" source) and that you may even be familiar with already, or it's something that you actually don't want to look at because you would infringe a competitor's (former employer's) intellectual property. The latter would mean that you wouldn't want to hire that person anyway.
If you can get the source online, do so. If you can verify the applicant's contribution to a well-known software (including proprietary software) by his name somewhere in the credits, do so.
In every other case, simply ignore whatever he sent you. It's either not worth looking at, or illegal, or high risk.

About 1)

The applicant sent you something because you gave him an assignment. If you have any competence (which I assume you do!), then for a typical programming assignment (...that you even chose yourself!), you will be able to tell whether it's a plausible solution that looks as if it might work by looking at the source code for less than 30 seconds (more likely 10 seconds).

If you cannot tell that the program will probably work (or what it's doing at all) within 30 seconds, the one who wrote it not the kind of person you want to hire, fullstop. You want people who write code that other humans can understand and maintain. You do not want someone who is trying to get smart at you, nor someone who regularly wins the obfuscated C contest. It doesn't even matter whether the program works. As soon as another person cannot understand the code, it never "works".
If the program looks like it will probably work, but you find anything that looks "weird" (say, Java unicode escape sequences, C++ raw string literals, stuff that looks like trigraphs, whatever), treat the assignment as "fail", move on to the next applicant. It's not necessary to include anything the like in 99% of all programs (and, sure enough, not in your assignment -- I should hope). So if you find anything "weird" like that, the applicant is not someone you will want to hire.

If the code passes that first triage, you may want to spend another 2-3 minutes looking at it more thoroughly. If you are still pleased with what you see after that, you may run it through a static analyzer and compile it in a virtual machine at a high warning level.

That should bring up issues that you may have missed while reading the source (such as invoking undefined behavior or narrowing conversion).
Compiling will first and foremost tell you whether the applicant has the necessary diligence and attention to detail, not so much whether he has programming skill. Much like writing the employer's name correctly on your application and spellchecking your CV before handing it in, it is best practice that you make sure whatever source code you hand in compiles without errors (and preferrably without warnings). If someone fails to do that, you do not want to hire him.

The risk of evil things happening at this point (exploiting the compiler and breaking out of the VM) is neglegible, seeing how you have already run a plausibility check over the code. Not going to happen.

0

If the possibility worries you, take an older machine (don't most of us have a few sitting around?), install current version of Linux and compiler &c, copy the source code to it, unplug the network cable (or turn off WiFi), and do the compiles. If anything nasty does happen, it won't* affect anything else.

And for malware in the Makefile, run it with the -n flag (IIRC, RTMF) to see what it will do without actually doing it.

*Unless of course your programmer coded the malware so that it waits for a reconnection, but in that case you a) wipe the machine; and b) forward the guy's resume to the NSA, 'cause he's wasted in the commercial world :-)

0

The bottom line is that there is risk. The risk is fairly small as other answers note, but there is a risk. That means you need to ask two questions:

  1. What can I do to mitigate the risk?
  2. Is the risk high enough that I should care?

The second is what you've posited here in this question, but it's the wrong focus for this particular case. The answer to mitigating the risk is clear and readily available: don't compile the code on your machine. You have two obvious ways of compiling it without using your machine:

  1. Use a virtual machine (as @FlorianMargaine pointed out immediately in the comments). You simply snapshot it before compiling and then restore the snapshot when you're done.
  2. Use a hosted service (such as an online compiler).

These ways of mitigating your risk are so obvious, cheap, and easily accessible that it's not worth spending a lot of time trying to analyze how big the risk is. Just do one of them and be done with it.

0

Visual Studio actually warns you if you open a project from an untrusted location (e,g, downloaded or network share).

One example how this could be exploited would be with a WPF project: You can reference .NET classes from XAML, and to provide IntelliSense, VS loads and executes the referenced classes at design time.

That means an attacker can drop a malicious .dll into the bin directory, replace the source code with non-malicious one, and at design time, the DLL is executed. After your first build, every trace of the malicious binary is gone.

So even though all provided code is "clean", the compiler is bug-free and you, of course, never manually execute any provided .EXE, malicious code could still be executed in the background. (To be safe from that specific attack, you can just make sure that there are NO binaries in the directory tree before opening the solution. VS will then prompt you to build the solution before providing IntelliSense at design-time.)

Similar vectors probably exist with other languages / OSes.

0

Reading source code: totally safe. Compiling source code: totally safe. Executing compiled binaries: well... that depends.

Compiling is just the computer reading the source code and writing its equivalent in binary form. After compilation, you just have 2 documents: one human-readable, and another computer-readable. Unless you get the computer to read (ie run) the 2nd document, nothing is going to happen.

9
  • 3
    Any explanation of why compiling is totally safe? One can pwn a server by sending a cleverly crafted message - why can't he pwn a compiler by giving it a cleverly crafted input?
    – sharptooth
    Jun 23, 2015 at 12:14
  • 2
    I think you'd notice cleverly crafter code designed to exploit a vulnerability in a server, or a compiler. But taking this to the extreme, why take the risk of viewing the code at all?! There are several exploits in editors that can be exploited just by viewing a file.
    – gbjbaanb
    Jun 23, 2015 at 12:20
  • 2
    This answer is total gibberish. There is no reason at all why a compiler would be inherently safe, or at least safer than something that does a simple task like setting up a SSL connection, yet recently a popular library contained a vulnerability. I would even argue that, because compilers are generally not used in a hostile environment (like the internet), they are less checked for vulnerabilities, and therefore more likely to have them.
    – Dorus
    Jun 23, 2015 at 14:40
  • 1
    Not so sure about compiling code being totally safe. Even in Java, with Maven (for example) a careless "mvn package" can pull stuff and perform tasks with additional plugins that you might not easily know about. I'm sure the same could apply to other build systems.
    – Bruno
    Jun 23, 2015 at 14:49
  • 1
    A Turing-complete language may allow a program to spend an unbounded amount of time to compile, if the compiler is allowed to run for an unbounded amount of time, but many compilers will create a bounded number of threads regardless of anything that might appear in the code being compiled, meaning that the CPU load from trying to compile one program at a time would be limited. A potentially bigger problem would be disk space requirements; it's entirely plausible that a 1KB source file might generate many gigs of object code.
    – supercat
    Jun 23, 2015 at 20:36
-1

I think you're worried about one of two flavors:

  • sophisticated, exploit-driven malware: unlikely, especially because these are targeted at very specific hardware and/or software and [based on your question] your attacker probably doesn't have that level of system knowledge.
  • things that screw with your environment: malicious prank directives (e.g. deleting your home directory) or inconsiderate/incompetent directives which change your system behavior (e.g. rewriting PATH or LIBRARY environment variables)

Some people have suggested virtual machines or old systems, but I offer a much easier solution: Compile as a different user with reduced/different permissions. Much easier than setting up a virtual machine or a dedicated computer.

If in the unlikely even that your system gets pwned by compile-time exploits, restore from backups (you have those, right?).

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