We’re moving!

We’ve got some exciting news to announce today: Doug DePerry and I are moving over to join the security team at Yahoo. We’ve known Alex Stamos, VP for Information Security at Yahoo, for quite some time. We first met Alex through the security community and greatly admire the efforts he and his team are making to ensure the security of Yahoo’s +800M users globally.

I founded Leaf Security Research in 2011, and working with our clients and partners has been an incredibly rewarding experience for me. We’ve done groundbreaking work for customers across the financial sector, tech startups, government and everything inbetween. Now it’s time for the next adventure. We look forward to taking our experience to Yahoo and staying in touch with the amazing network we’ve built up over the years.

Chris Rohlf

July 25th, 2014

Secure Commit

Leaf SR - Secure CommitWe here at Leaf SR routinely discover high impact security vulnerabilities and architecture security issues for our customers: everything from insecure memory allocators to privilege escalation via deserialized web-app session cookies. We love tackling the challenges that solving these problems requires. We’ve crushed hundreds of bad security vulnerabilities in our time and learned many valuable lessons along the way. We believe very deeply in a few of those security lessons and integrate them into our work for customers every day:

  • The earlier security enters the development cycle the better. Secure designs are more resilient and easier to maintain in the long run
  • Staying current with exploit techniques and attack trends is critical to defending and hardening the most sensitive applications and code
  • Having a plan in place to tackle security issues as they arise is one of the best investments you can make in your application

Today we are announcing ‘Secure Commit‘, which encompasses all of the services we offer at Leaf SR. Secure Commit takes everything we have learned over the years and makes it available to developers, security, and operations staff at any stage of the software development life cycle. Whether you’re still in the planning stages or have already begun committing code, we can apply our expertise to the project. We accomplish this through detailed architecture assessments, defensive designs, and application testing.

We also understand that legacy applications are still prevalent and require a different approach to reduce their potential risk. There are steps that can be taken to discover and reduce vulnerabilities in legacy applications while minimizing the impact of those that remain hidden.

By applying the principles we have rolled into Secure Commit we believe the end result can be more secure code with lower security debt over time.

My heart is ok, but my eyes are bleeding

TL;DR: heartbleed is bad, but not world ending. OpenSSL is not any more vulnerable because of its freelists and would still be vulnerable without them.

We felt that there weren’t enough heartbleed write-ups yet, so we wrote another one. Unlike many of the other posts, we are not going to talk about the TLS protocol or why we think the heartbeat extension is pointless. Instead, we are going to focus on the bug itself and more specifically, why sensitive data gets leaked.

First we would like to state that, as far as complexity goes, the heartbleed vulnerability is nothing special, but that doesn’t mean it was easy to find. All bugs are easy to spot after someone else points them out to you. Hindsight is 20/20 after all. Riku, Antti and Matti at Codenomicon and Neel Mehta at Google all independently discovered this bug. Neel was also kind enough to sanity check this post before it went live (thank you Neel!) Whatever your feelings on vulnerability disclosure are, you should thank them for finding the bug and giving us all something interesting to talk about.

All of the code in this post is from openssl-1.0.1c, which is what I had running on an old virtual machine with Apache. First, let’s discuss some important OpenSSL data structures. For this discussion the ‘top’ level structure is SSL, which is defined in ssl.h as ssl_st with a typedef. Within this structure is a pointer s3 of type SSL3_STATE, which is a typedef of ssl3_state_st. Inside that structure is another structure of type SSL3_RECORD, which we reference as rrec. An SSL3_RECORD contains type and length values among other fields. The SSL3_STATE structure also contains rbuf, and wbuf of type SSL3_BUFFER. The SSL3_BUFFER structure contains a pointer to data buf, and len/offset/left members to track its usage. These buffers are allocated and deallocated often during a TLS exchange. For performance reasons, the OpenSSL developers wrote a separate freelist implementation to store them. This freelist implementation is apparently buggy but fairly easy to understand (more on this below). The freelist is actually two lists, one for read buffers (rbuf) and one for write buffers (wbuf). These lists are accessed via the SSL_CTX structure that contains wbuf_freelist and rbuf_freelist respectively. We reference the SSL_CTX structure via a pointer within the SSL structure. Heres some pseudo code to make this less confusing. Some variables have been cut for brevity.

struct SSL {
  SSL_CTX *ctx;
  SSL3_STATE *s3;

struct SSL_CTX {
  SSL3_BUF_FREELIST wbuf_freelist;
  SSL3_BUF_FREELIST rbuf_freelist;

struct SSL3_STATE {
  SSL3_BUFFER rbuf; /* read IO goes into here */
  SSL3_BUFFER wbuf; /* write IO goes into here */
  SSL3_RECORD rrec; /* each decoded record goes in here */
  SSL3_RECORD wrec; /* goes out from here */

  size_t chunklen;
  unsigned int len;

struct SSL3_BUFFER {
  unsigned char *buf; /* at least SSL3_RT_MAX_PACKET_SIZE bytes,
                       * see ssl3_setup_buffers() */
  size_t len; /* buffer size */
  int offset; /* where to 'copy from' */
  int left; /* how many bytes left */

struct SSL3_RECORD {
  /*r */ int type; /* type of record */
  /*rw*/ unsigned int length; /* How many bytes available */
  /*r */ unsigned int off; /* read/write offset into 'buf' */
  /*rw*/ unsigned char *data; /* pointer to the record data */
  /*rw*/ unsigned char *input; /* where the decode bytes are */
  /*r */ unsigned char *comp; /* only used with decompression - malloc()ed */

So now that we understand some basic structures lets examine the vulnerability. Heartbleed is an out-of-bounds read in the OpenSSL TLS Heartbeat implementation. The tls1_process_heartbeat function is responsible for parsing a TLS heartbeat message. On line 2586 a pointer to the payload is assigned from s, which is a pointer to a structure of type SSL. This structure contains a pointer s3 of type SSL3_STATE. Within the structure pointed to by s3 is an array of SSL3_RECORD types which we reference as rrec. The pointer p now points at the first record. On line 2593 a length value is extracted from the record and assigned to the variable payload [1]. On line 2610 some memory is allocated for the response message. The call to OPENSSL_Malloc allocates enough memory for (19+payload) bytes, the return value is assigned to the pointer buffer. On line 2616 a call to memcpy will copy from pl (which points at the record just beyond the type and length fields) into bp (the buffer we just allocated on line 2610) exactly payload number of bytes.

2584 tls1_process_heartbeat(SSL *s)
2585 {
2586   unsigned char *p = &s->s3->rrec.data[0], *pl;
2587   unsigned short hbtype;
2588   unsigned int payload;
2589   unsigned int padding = 16; /* Use minimum padding */
2591   /* Read type and payload length first */
2592   hbtype = *p++;
2593   n2s(p, payload);
2594   pl = p;
2596   if (s->msg_callback)
2597     s->msg_callback(0, s->version, TLS1_RT_HEARTBEAT,
2598     &s->s3->rrec.data[0], s->s3->rrec.length,
2599     s, s->msg_callback_arg);
2601   if (hbtype == TLS1_HB_REQUEST)
2602   {
2603     unsigned char *buffer, *bp;
2604     int r;
2606   /* Allocate memory for the response, size is 1 bytes
2607   * message type, plus 2 bytes payload length, plus
2608   * payload, plus padding
2609   */
2610   buffer = OPENSSL_malloc(1 + 2 + payload + padding);
2611   bp = buffer;
2613   /* Enter response type, length and copy payload */
2614   *bp++ = TLS1_HB_RESPONSE;
2615   s2n(payload, bp);
2616   memcpy(bp, pl, payload);
2617   bp += payload;
2618   /* Random padding */
2619   RAND_pseudo_bytes(bp, padding);
2621   r = ssl3_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload + padding);

So the bug here is that the size of the record pl points at does not have to match the value of payload. Sending a TLS heartbeat message with a length value larger than the actual size of the record will cause the memcpy to read beyond the bounds of the record and return whatever is in memory after the record to the user. This bug is simple and already well documented else where. But lets take a step back and focus on that OPENSSL_malloc call. OPENSSL_malloc is a macro that calls CRYPTO_malloc in mem.c. CRYPTO_malloc calls malloc_ex_func which is a function pointer that can be configured in OpenSSL for calling a different malloc implementation:

static void *(*malloc_func)(size_t) = malloc;
static void *default_malloc_ex(size_t num, const char *file, int line) { 
return malloc_func(num); }
static void *(*malloc_ex_func)(size_t, const char *file, int line) = default_malloc_ex;

If we look at the function prototypes above we see that default_malloc_ex returns malloc_func which is a function pointer to… wait for it… malloc. This may come as a shock to you but there seems to be some confusion on the internet about what memory is exposed when triggering this bug. More on this in just a few paragraphs.

So we know CRYPTO_malloc can boil down to a call to malloc, and does by default. This is where our data gets written to but not where we read it from. In order to understand why sensitive data gets leaked we need to understand the freelists that store the records where we are copying data from. If we look at SSL_CTX_new we see the following code that sets up these freelists for the first time. Most of the members are initialized to 0 with the exception of wbuf_freelist and rbuf_freelist which are assigned the return value of a call to OPENSSL_malloc.

1677 SSL_CTX *SSL_CTX_new(const SSL_METHOD *meth)
1678 {
1828   ret->freelist_max_len = SSL_MAX_BUF_FREELIST_LEN_DEFAULT;
1829   ret->rbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
1830   if (!ret->rbuf_freelist)
1831   goto err;
1832   ret->rbuf_freelist->chunklen = 0;
1833   ret->rbuf_freelist->len = 0;
1834   ret->rbuf_freelist->head = NULL;
1835   ret->wbuf_freelist = OPENSSL_malloc(sizeof(SSL3_BUF_FREELIST));
1836   if (!ret->wbuf_freelist)
1837   {
1838     OPENSSL_free(ret->rbuf_freelist);
1839     goto err;
1840   }
1841   ret->wbuf_freelist->chunklen = 0;
1842   ret->wbuf_freelist->len = 0;
1843   ret->wbuf_freelist->head = NULL;

This is allocating the first entry in the rbuf_freelist and wbuf_freelist using a call to OPENSSL_malloc, which we know calls down into malloc. So we know the memory that backs these lists is originally allocated with malloc, so it lives along side other regular chunks allocated by code that called malloc directly. Chunks get added to the freelist using freelist_insert and retrieved from the freelist using freelist_extract. When the freelist_extract function is called for the first time the list chunklen is 0 (see above), which means line 688 will not be reached, leaving ent and result = NULL which means the call to OPENSSL_malloc on line 698 will happen. This just returns a chunk from the heap managed by malloc. If these conditions are met then the list has existing free chunks available for use that match the requested size. This chunk is handed back to the caller and the size of the list is decremented by one, if this was the last chunk in the list then the chunklen is reset to 0.

678 static void *
 679 freelist_extract(SSL_CTX *ctx, int for_read, int sz)
 680 {
 681   SSL3_BUF_FREELIST *list;
 683   void *result = NULL;
 686   list = for_read ? ctx->rbuf_freelist : ctx->wbuf_freelist;
 687   if (list != NULL && sz == (int)list->chunklen)
 688     ent = list->head;
 689   if (ent != NULL)
 690   {
 691     list->head = ent->next;
 692     result = ent;
 693     if (--list->len == 0)
 694     list->chunklen = 0;
 695   }
 696   CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
 697   if (!result)
 698     result = OPENSSL_malloc(sz);
 699   return result;
 700 }

When a caller is done with a chunk it retrieved from the list a call to freelist_insert is made. This function first checks to see if the size requested is the same as the list chunklen or if the list chunklen is 0. A further check to see if the list len is already at freelist_max_len (32 by default) and size is greater than sizeof(SSL3_BUF_FREELIST_ENTRY). If these conditions are satisfied then the list chunklen is set to the requested size (it may have been 0 if not previously used), ent is assigned the value of the chunk to be inserted, the next pointer is set to the head, the list head is set to ent, the list size is incremented, and finally the mem pointer is set to NULL. If these conditions were never initially met then mem would be free’d via a call to OPENSSL_free on line 725.

 702 static void
 703 freelist_insert(SSL_CTX *ctx, int for_read, size_t sz, void *mem)
 704 {
 705   SSL3_BUF_FREELIST *list;
 709   list = for_read ? ctx->rbuf_freelist : ctx->wbuf_freelist;
 710   if (list != NULL &&
 711     (sz == list->chunklen || list->chunklen == 0) &&
 712     list->len < ctx->freelist_max_len &&
 713     sz >= sizeof(*ent))
 714     {
 715       list->chunklen = sz;
 716       ent = mem;
 717       ent->next = list->head;
 718       list->head = ent;
 719       ++list->len;
 720       mem = NULL;
 721     }
 723     CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
 724     if (mem)
 725       OPENSSL_free(mem);
 726 }

The freelist can help speed up the performance of OpenSSL connections if a specific buffer size is repeatedly requested. The freelist (both read and write) don’t care what the contents of those chunks are or what callers invoke them. These buffers are normally setup by ssl3_setup_buffers (which calls ssl3_setup_read_buffer and ssl3_setup_write_buffer), and accessed as s3->rbuf, and finally passed back via ssl3_release_read_buffer and ssl3_release_write_buffer. Ok so we now understand the basics of the SSL freelist(s).

So why is any of this interesting when it comes to heartbleed? Well the internet is on fire with proof of concept code for the bug that reliably leaks sensitive session credentials and supposedly private keys. In order to understand what will be leaked we only need to look at our ssl->s3->wbuf.buf and ssl->s3->rbuf.buf pointers. We know these may be stored in the freelist along side other chunks of the same exact size. We also know the freelist has a max size, which is 32 by default. So the easy way to start allocating records from outside of this list is to simply fill up the list via multiple connections or make several requests of different sizes. The latter works because the list only holds chunks of a specific size.

So while OpenSSL does utilize a freelist for these records they are still backed by memory returned by malloc which means they may live adjacent to other memory that is held and used by other components such as the web server that is using OpenSSL. This is why we see sensitive data when we trigger the bug. Another reason we can leak sensitive data is that OpenSSL performs the decryption of these records ‘in place’. This means that the SSL record buffers are decrypted in their current location in memory.

There is currently some discussion on how OpenSSL mitigated the security mitigations of default heap allocators by introducing their own freelist. I mostly disagree with this view.

  • For starters the freelist is backed by the system malloc (by default). They did not implement their own vulnerable malloc() from scratch.
  • Similar freelist implementations and designs can be found in most moderately complex applications you’re running on your desktop right now.
  • This vulnerability would still be exploitable, and would still leak sensitive chunks of memory with or without the freelist.
  • Ironically if you wanted to leak data from the general heap the freelist is something you have to get around first.

The points above are an important take away for understanding exploitation and the overall severity of a vulnerability [2].

In conclusion, heartbleed is bad, but don’t panic. The OpenSSL code may be a mess but its had several remote code execution vulnerabilities in recent years that no one paid too much attention to. The internet is still here, you can still securely browse your email that no one else cares about, and the food supply is plenty.

Written By: @ChrisRohlf

[1] Naming a length variable ‘payload’ is borderline sociopathic

[2] In our Advanced C/C++ Source Code Analysis training we talk a lot about memory allocation and how it affects the severity of bugs. We focus on where objects are allocated in memory and why it is important to understand how their lifecycle is managed and what other objects and data structures are managed along side them.

Training At Black Hat USA 2014

Once again we will be delivering our ‘Advanced C/C++ Source Code Analysis‘ course at this years Black Hat USA event. We give this course often to our private customers throughout the year and not a single delivery of the content goes by without some sort of improvements to the material. We are constantly striving to keep the examples, slides and lectures up to date. The C/C++ languages may be older but we want to make sure we are covering relevant material, and that means staying current and inline with what topics our students want to learn about. We are updating the course for this years Black Hat event with vulnerabilities in recent versions of Nginx, WebKit/Blink, C++11, and more.

This isn’t a secure coding class, and we don’t cover strcpy. If you want to understand object lifecycle management via the rule of five, or track down and analyze complex use-after-free vulnerabilities that are the result of missing reference counting mechanisms provided by overloaded assignment operators then you want to sign up for the course. We promise you will leave with a different perspective on source code auditing and a little more confident that you can svn checkout the latest WebKit and start finding 0-day. See you in Las Vegas!

BlackHat WebCast: C/C++ AppSec In 2014

I will be giving a free BlackHat webcast on January 30th, 2014 at 2 EST. The topic I’ll be discussing is “C/C++ AppSec in 2014”. Many people assume appsec pertains only to web applications and mobile apps, but appsec is a general concept that applies to any application written in any language. Even though C and C++ are older languages, they remain ubiquitous and important to enterprise and startup companies alike. During the webcast I will be discussing the evils of exploitable memory corruption bugs and what appsec looks like in modern C/C++ applications. You can read the full description on the BlackHat website.

GCC Poison

Microsoft has taken a more proactive security stance in its developer toolchain since the inception of its SDLC in the mid-2000’s. One of the earlier steps their SDLC used to reduce vulnerable code was to introduce a header file named banned.h. What this header file does is use an MSVC pragma to deprecate functions that developers typically use incorrectly, and consequently implement remote code execution vulnerabilities in their applications. These functions are the usual suspects: strcpy, sprintf, memcpy, CopyMemory, RtlCopyMemory, strcat, alloca and many more Win32 specific stuff. Way back in its initial release banned.h had similar support for GCC, but it was removed sometime in 2008. I don’t recall exactly why or when but it doesn’t make much sense for Microsoft to maintain that functionality anyway.

Even today in 2013 much of the legacy software that is still critical to many enterprises contains code that calls these vulnerable library functions. Despite modern memory protections like ASLR and DEP the vulnerabilities these functions introduce are still exploitable under the right conditions.

Unfortunately there still doesn’t seem to be an equivalent to banned.h available for projects using GCC so I started a project page for one at GitHub. It uses the GCC poison pragma which bans a specific identifier from your code. As far as I know GCC does not recommend using pragma’s (but to be fair I don’t recommend using GCC when Clang is an option). There are some caveats to this approach though, if the identifier was part of an expanded macro before the identifier was poisoned then it will not throw an error. This is by design because you don’t want GCC throwing errors for unsafe calls made in header files beyond your control. Heres an example of how the pragma works:

#include <stdio.h>
#include <string.h>

#pragma GCC poison strcpy

int main(int argc, char *argv[]) {
  char buf[10];
  strcpy(buf, argv[1]);
  return 0;

$ gcc -o string string.c
string.c: In function ‘main’:
string.c:8:2: error: attempt to use poisoned “strcpy”

It’s simple but it can be very helpful when you’ve got a lot of legacy C code sitting around that you want to remove these functions from and keep developers from committing and compiling new code that contains them. For the initial release of gcc-poison we have created a small list of unsafe C functions in the form of a header file you include your source just like you do with banned.h. I must emphasize ‘small’ here for a couple of important reasons.

Microsoft can ban many more simple functions because their libc has secure equivalents, but GNU libc lacks them so our list isn’t as comprehensive. A good example of this is the memcpy function. It is arguably the source of a large number of buffer overflows but it simply isn’t practical to remove it from most programs. Fixing these issues requires functionality that the poison pragma does not provide such as compiler extensions and secure equivalents. The new C11 standard defines some of these secure replacement functions in the ‘Bounds Checking Interfaces’ section but as far as I can tell the glibc maintainers have opted not to implement them and they are outside the scope of GCC development.

The goal of using our poison.h (or something like it) is to help secure legacy code. It is far from complete but you should be able to use it in internal projects by adding all those custom API’s you deprecated in 2004 but can’t seem to stop developers from using.

Comparing ASM.js and NaCl

OK so I am a few months late to the ASM.js / Native Client (NaCl) debate. Thats OK because most of what has been said so far that compares them is terrible. If you’re unfamiliar with either technology then please see this and this first or this post won’t make much sense. The two competing technologies have a similar goal: to bring native code to the web. But they both approach solving this problem in very different ways.

Whats the problem with the web in its current form?

Since the mid 2000’s or so there has been this big push to take all those legacy applications and shove them into an HTML form tag with some slick CSS. Great idea in theory except it doesn’t really work for all those millions of lines of existing C/C++ applications. And then theres applications like games and office/productivity suites that require a greater level of user interactivity and of course performance. We aren’t arguing you can’t do amazing things with Javascript. We are simply trying to say that the heavy lifting almost always comes down to native code and theres already piles of existing C/C++ sitting around. Unfortunately the only available scripting environment in your browser is JavaScript. And although JIT engines have made it very fast in recent years going from the power of C++ to JavaScript is still like programming with a hand tied behind your back. JavaScript lacks the typing system necessary to do low level bitwise operations. No sorry, charCodeAt doesn’t begin to cut it. Perhaps the most hilarious solution to this problem is the JavaScript typed array. Which not only allows you to boot the Linux kernel in your browser but also craft precise heap feng-shui payloads which were rather ugly with regular strings. Unfortunately typed arrays only take you so far because in the end you’re still writing JavaScript, and thats no fun. There has to be a better way to do this…

What is ASM.js?

It’s a virtual machine that lets you compile C/C++ programs to JavaScript and then run them in a browser. This process is done by using LLVM to turn C/C++ code into LLVM bitcode which is then fed to Mozilla’s Emscripten, which turns it into JavaScript. If a browser follows the asm.js specification (a strict subset of JavaScript) it can benefit from some AOT compilation that will optimize the generated native code. Compiling one language to another is not new, its been done many times before. Because many different languages can be compiled into LLVM bitcode the future holds a lot potential use cases for ASM.js.

Each ASM.js application lives within its own virtual machine whose entire virtual memory layout is contained within a JavaScript typed array. The ASM.js specification is technically a subset of JavaScript which means not-JavaScript since you have to support it explicitly to get the benefits (see above). ASM.js has a syntax you can read/write as a human, its not a bytecode, and it is pretty close to JavaScript. The benefit of ASM.js is that fast JavaScript interpreters already exist. It ‘just works’ today, but will only benefit from the AOT compilation as long as your JavaScript interpreter supports parsing the ASM.js specific syntax… which only Firefox nightly supports right now…

One of the key things this subset of JavaScript does for you is eliminate the JavaScript JIT’s requirement of generating code that operates on data that could change types at any moment. When your browsers JavaScript JIT compiler looks at a piece of JavaScript code like this…

function A(var b, var c) { 
return b + c;

A('wat', 'ok');

…the JIT doesn’t know the types of the b or c variables. The way to solve this is to generate code for the function A that can operate on strings, integers and other types supported by the engine that can make use of the + operator. On the browser back end you’ve got to store those variables as tagged data structures that indicate their type. I’m speaking generically here, the implementation details of different JITs will vary. But you can probably already see how this slows things down but is a requirement when generating native code for a dynamically typed language like JavaScript. In ASM.js some of these restrictions are lifted because it is dealing with C/C++ code that doesn’t support dynamic types the way JavaScript does. When you declare an int its an int. Unless of course you cast it as something else but those cases are usually explicit and the compiler knows about them ahead of time. If you’re interested in the details of JIT engines and how they deal with this kind of thing then I highly recommend this blog.

You may have guessed by now that programs such as games are complex and have visual components that normally render on a GPU. For this we have WebGL. But then theres audio, networking, file access and threads. It sounds like tackling this problem is going to require some heavy lifting and a very rich API…

What is Native Client (NaCl)?

We have talked about NaCl a lot on this blog but thats because its a novel approach to a very hard problem, namely executing native code in a memory safe way on architectures like x86. Its not another reinvention of malicious code detection, its a unique approaching to sandboxing all code while still giving it access to the raw CPU. NaCl does not transform existing code, it takes straight x86/x86_64/ARM instructions from the internet, validates them and executes them. This is a fundamental difference between ASM.js and NaCl. The former takes C/C++ and turns it into JavaScript which gets put through a JIT engine, the latter executes the native instructions as you provide them to the browser.

There are of course some trade offs such as a somewhat complicated build process, but Google has gone out of their way to ease this pain. Portable Native Client (PNaCl) takes this a step further and allows developers to ship a bitcode for their application which PNaCl will compile on the fly for your particular architecture. In this sense PNaCl seems a bit closer to the ASM.js model on the surface, but underneath its an entirely different architecture. The code PNaCl produces exists within a sandbox and must adhere to the strict rules NaCl imposes.

NaCl also provides an extensive and rich API through PPAPI for doing heavy lifting like graphics, audio and threads. We aren’t going to cover the NaCl architecture too deeply here, see our older blog posts for that. Instead we’ll move onto our security comparison of NaCl and ASM.js

What security benefits/tradeoffs do they introduce?

Every new line of code adds attack surface, these two components are no different. Because ASM.js is a strict subset of JavaScript there will be new parser code to look at. The exploitation properties of ASM.js remain to be seen as its only in Firefox nightly right now. My first guess is it makes JIT page allocation caps in Firefox impossible. I’m also confident you can use it to degrade, if not completely defeat, ASLR. In our 2011 JIT attacks paper Yan Ivnitskiy and I discussed using JavaScript to influence the output of JIT engines in order to introduce specific ROP code sequences into executable memory. I suspect the faster JIT pipelines used to transform ASM.js code into native code will make this process easier at some point but I have no data to prove that right now.

The approach of taking an unmanaged language like C/C++ and converting it to a managed and memory safe one solves the memory corruption issues in that particular piece of code. So if some C++ code originally contained a buffer overflow, once converted and running in ASM.js you would be accessing a JavaScript type which comes with builtin runtime checks to prevent such issues. Instead of overwriting some critical data you would trigger an exception and the browser would continue on as if nothing ever happened. As a side point, it may be fun to write an ASAN-like tool from ASM.js that tests and fuzzes C/C++ code in the browser using the JavaScript runtime as the instrumentation.

NaCl offers more than just bringing existing C/C++ code to your browser. NaCl has a unique sandbox implementation that can be used in a lot other places. NaCl statically validates all code before executing it. This validation ensures that the module can only execute code that adheres to a strict set of rules. This is a requirement because NaCl modules have full access to the CPU and can do a lot more than JavaScript programs currently can by utilizing the PPAPI interfaces in Chrome. This validation and code flow requirements have performance overhead but not enough that it makes large applications unusable.

I still think we will see NaCl on Android one day as a way to compete with iOS code signing. NaCl is such a game changer for browser plugins that I believe it will influence sandbox designs going forward. If you’re designing a sandbox for your application today you should have read its design documents yesterday.

ASM.js does not provide a sandbox, or any additional layer of security for that matter. It’s just a toolchain that compiles a source language into a subset of JavaScript. It relies on the memory safe virtual machine provided by the existing JavaScript interpreter to contain code. We all know how well that has worked out. Of course I’m not picking on Mozilla here, any browser without a sandbox is an easy target. But this is where the main difference between ASM.js and NaCl becomes apparent. ASM.js never intended to increase the security of Firefox, only bring existing C/C++ code to a larger web platform. NaCl intends to do this as well but also to increase the security of Chrome by containing all plugins in two sandboxes. I suppose if one were to take a plugin like Adobe Flash Player and port it to ASM.js this is a security benefit to Firefox users but in the end they are still without a sandbox around their JavaScript engine and probably paid a performance penalty for it too.


ASM.js is still under heavy development, so is NaCl/PNaCl, but the latter is a bit more mature at the moment given its end goal. For ASM.js to run existing C/C++ programs and libraries it will need to provide an extensive API that does a lot more than current JavaScript interpreters provide. NaCl already currently offers this along with better performance and a stronger sandbox design. If Mozilla wants to get serious about running all untrusted code from the internet in its JavaScript interpreter then it needs a real sandbox ASAP.

Finally, if the future is recompiling everything to JavaScript then the future is ugly.

Update 6/3/13: Fixed some ambiguous text that implied asm.js only worked in Firefox. See above for details.