Home Careers Contact Blog Product Services Team The Security Implications Of Google Native Client Chris | May 15th, 2009 | Filed Under: New Findings What would it look like if Google tried to unseat Flash and obsolete all desktop applications? It would look a lot like Google Native Client (NaCl): a mechanism to download a game written in C/C++ from Id Software and run it in your browser, without giving Id Software the ability to take control of your computer. Google NaCl is, on its face, a crazy-talk idea. It’s a browser plugin that downloads native x86 code from a website and runs it on your machine. If this sounds familiar, it’s because Microsoft tried it over a decade ago with ActiveX. If you’re skeptical about the idea, it’s because ActiveX was a security calamity; O.K. an ActiveX control, and it owns your machine almost completely. So the primary obstacle between Google and the future of software delivery is security. Google has a lot of interesting ideas about how to overcome that obstacle. But security people unlikely to take Google’s word for it. So Google held a contest: “we’ll publish the source code, you’ll find flaws. The winner gets $0x2000 USD.” We took the bait. And things were looking great for us. Then Skynet noticed Google, decided it was a threat, and sent a Mark Dowd unit back through time to terminate it. The contest winner hasn’t been declared yet, but as we aren’t a murderous robot made out of liquid metal, we’re guessing we didn’t take first place. But we learned lots of stuff in the process, and so we have stuff to say. And when you get right down to it, isn’t that worth a lot more than money? Think about that, while we share some lessons about NaCl and cry into our discount beers. What is NaCl, and why do I care? Absent any innovations in browsers or HTML, within 5 years, every application normal people use will be run off a web server and rendered via Javascript, HTML, or Flash. That trend is inexorable. You know it and I know it. But what if we want to ship a game title? We need a faster runtime, real graphics, and a decent interaction model. Until recently, if we wanted to run “real” programs behind a browser, we had two options: ActiveX and Java. Consider ActiveX. It’s a really simple idea. In your HTML code, you can specify a URI to a library file. The same kind of libraries your desktop applications use. They’re written in whatever language makes sense to the authors, but they’re delivered as native X86 instructions. They talk to your computer the same way any application does. This is a powerful concept. Virtually anything a desktop app can do, an ActiveX control can do. Unfortunately, read that last sentence again. So that’s a problem. Most security-conscious people aren’t willing to trust random native executables running on their computers off web pages. Consider Java. Java does something that ups the security ante significantly. Instead of delivering raw X86 opcodes, it’s delivering JVM bytecode. The Java plugin either interprets or compiles that bytecode, but either way, the actual instructions executing on your computer and talking to your OS belong to Java. You only have to trust the one native program. But wait, there’s more! Because Java programs don’t execute directly on the CPU, there’s an architectural opportunity to improve security: you can put a security layer in between the program and the operating system. Java calls this the “applet sandbox”. The applet sandbox basically says, “applets can’t talk directly to the OS; instead, they have to use a set of interfaces we created specifically for allowing applets to talk directly to the OS”. You can’t do this with ActiveX, because you’re executing raw X86 instructions. From userland, on an X86 chip, with any reasonable performance, there’s no way to put a layer between Win32 userland (or OS X) and the kernel. If you want that layer there, you have to not execute code off the Internet directly on the CPU. But wait, there’s still more! Because the JVM designers got to design their own virtual machine and controlled the whole runtime, they were able to make Java bytecode very “regular” and very easy to analyze. Java is so easy to analyze that 99% of compiled Java binaries can be decompiled right back to source code. X86 is not regular and easy to analyze. For one thing, X86 instructions come in various shapes and sizes. The shortest X86 instructions (such as “INCR EAX”, or “add 1 to the EAX register”) are one byte long. Because X86 instructions can accept prefixes that change their meanings, the largest X86 instructions are over 10 bytes long. The irregularity of X86 instruction lengths does more than just make instructions tedious to recognize. It also means that, unlike in a RISC architecture where instructions are always, say, 32 bits long, an X86 instruction is not necessarily at an aligned offset in the file. Byte 1024 of an X86 executable is as likely to be the middle of an instruction as it is the beginning of one. Why does this matter? You can’t easily put a layer between a bare-metal X86 program and the OS kernel to keep that program from calling “execve” and running any program it wants. So, instead say you wanted to write an X86 “verifier” that would check programs you ran to make sure they don’t try to call “execve”. You’d have some problems. For starters, because X86 programs can jump pretty much anywhere in their instruction streams, even if you disassembled the program and checked for calls to execve, a malicious program could make a series of innocuous instructions which, when the program jumped into the middle of them, actually executed execve. Second, as anyone who’s ever written overflow shellcode knows, X86 programs can execute out of data. So even if you verified the instruction stream perfectly, the program could use innocuous instructions to create malicious instructions in data that the verifier couldn’t reasonable check. And now we come to the part where we explain why we’re telling you all of this. Behold, Google NaCl! Repurposing an idea from the mid-’90s, NaCl employs a very simple trick to make native X86 programs reliably verifiable. And if you can verify an X86 program, you don’t need a layer between the program and the OS: you can just have rules, and refuse programs that break the rules. The trick is: restrict X86 programs to those that are verifiable. What are those programs? Well, among other things: They must admit to simple disassembly, yielding a stream of recognizable opcodes. This wouldn’t be a strict requirement for an X86 program, but most programs adhere to it anyways. Those opcodes must not jump to anything but the beginning of an instruction recognized by that simple disassembly. Easy to say, tricky to implement, but not a huge design change for most X86 code. They can’t modify the program text itself. (Note that we’re butchering this concept a little; in the literature, it makes a big difference whether you patch a candidate program to safety, or whether you detect unsafeness and halt. But we digress.) With those constraints in place, it turns out NaCl can reliably analyze X86 instructions. The verifier can then add rules: You can’t muck with memory management to fool the verifier. You can’t talk directly to the operating system. Instead, you can call into trusted code in the first 64k of the binary that will make selected system calls for you, just like with a Java applet. There is a very important difference between what NaCl is doing and what Java is doing. Java’s security measures are chaperones. They’re always there and always checking your actions. NaCl’s mechanisms are just rules. They’re checked once, and then the program is on its own. NaCl promises to be faster than Java. More importantly, to build a NaCl program for your customers browsers, you don’t have to port to Java; you just have to use the NaCl build environment (a patched GCC that targets a simple ELF module) on your existing C code. Google, for instance, ported Quake. It is worth mentioning here —- and this is to NaCl’s credit —- that this isn’t a new idea. The fundamental approach dates back to Wahbe et al, from SOSP in 1993 (!). The core ideas that make the approach work on X86 (Write/Jump sandboxing vs. full Read/W/J isolation) are in an 05 MIT TR and Usenix article by Stephen McCamant (then a grad student at MIT) and Greg Morrisett at Harvard. And in 2008, Bryan Ford and Russ Cox released a version of the same idea, called Vx32, that runs on Linux and FreeBSD. Google NaCl would be the first mainstream implementation of the idea. That’s a bit scary, but if you generalize a little bit, the ideas at play here aren’t really all that different from the ideas VMWare relied on; in fact, Vx32 runs code out of basic block caches just like VMWare (and DynamoRIO before it) and oh my god we need to stop geeking out about this now. What could possibly go wrong? . First: assume the X86 verification all just worked.
Details
-
File Typepdf
-
Upload Time-
-
Content LanguagesEnglish
-
Upload UserAnonymous/Not logged-in
-
File Pages10 Page
-
File Size-