1 of 16

Sandboxing libraries in Chrome using SFI:

zlib proof-of-concept

Mark Seaborn

October 2013

2 of 16

Background

  • Chrome contains a lot of C/C++ code
  • Not memory-safe -- vulnerabilities:
    • Buffer overruns
    • Use-after-free()
  • Handles data from the Web
    • zlib, libpng, libjpeg, sqlite, NSS/OpenSSL, …
  • Can we sandbox these?
    • Native Client (NaCl) -- a sandbox based on Software Fault Isolation (SFI)
      • x86-32, x86-64 & ARM versions

3 of 16

Proof of concept: zlib proxy

  • Picked zlib:
    • Simple interface to proxy: byte streams
    • Simple dependencies: malloc()
    • It has had some buffer overruns
  • Two parts:
    • zlib-specific proxy: wrap inflate()
    • “MinSFI” sandbox
  • Can LD_PRELOAD into image viewer
  • Started prototyping using NaCl
    • Too heavyweight
    • Enter+leave sandbox: >500 cycles

4 of 16

MinSFI: main ideas

  • Trusted compiler
    • Add sandboxing into LLVM IR: early in pipeline
    • OK for Chrome: code not attacker-provided
  • Trusted call stack
    • Outside sandbox memory
    • So can’t overwrite return addresses
      • Allows RET: return prediction
    • Similar to asm.js
      • Separate stack for address-taken locals
    • No instruction bundles: no NOP padding
    • Safer for signal handlers

5 of 16

MinSFI: other benefits over NaCl

  • Runtime code is minimal
    • No assembly code; architecture-neutral
    • No need to copy/mmap() code at startup
  • Fast switches to/from trusted code
    • Can allow calling trusted functions directly
    • Could even inline trusted functions
  • Lower runtime overhead (potentially)

6 of 16

Sandbox goals

  • Memory accesses: confined to sandbox memory region (4GB)
    • 32-bit pointers
  • Function calls: limited to sandboxed functions

7 of 16

1: Sandbox memory accesses

Constrain accesses to sandbox address space

  • The security-enforcing part

Before:

int x = *ptr;

After:

// Bound (to 32 bits) and add base address�int x = (int *) (__sfi_sandbox_base + (uint32_t) ptr);

8 of 16

2. Global variables

  • Allocate inside the sandbox’s address space
  • Create template for initialisers

Before:

int global1 = 123;

int global2 = 456;

int *get_global1() { return &global1; }

int *get_global2() { return &global2; }

After:

int __sfi_globals_template[] = { 123, 456, ... };

int *get_global1() { return (int *) 0x10000; }

int *get_global2() { return (int *) 0x10004; }

9 of 16

3: Stack-allocated variables

Handle “alloca”: address-taken local vars

  • Allocate inside sandbox address space
  • Separate from call stack: two stacks

Before:

void foo() {� int x = 1;� bar(&x);�}

After:

char *STACK = 0x100000;�void foo() {� STACK -= 4;� int *x_addr = (int *) STACK;� *x_addr = 1;� bar(x_addr);� STACK += 4;�}

10 of 16

4. Sandbox indirect function calls

  • Add indirection: arrays of functions
    • One array per function type
    • Function pointers become indexes

Before:

typedef int (*func_t)(void);

int apply(func_t f) {

return f();

}

After:

func_t __sfi_funcs[] = { ... };

int apply(func_t f) {

if ((unsigned) f >= ARRAY_SIZE(__sfi_funcs))) abort();

return __sfi_funcs[(unsigned) f]();

}

11 of 16

Proxy

  • Sandbox startup
    • Allocate 4GB (+ guard region)
    • Copy data segment
  • inflateInit()
    • Allocate z_stream inside sandbox
  • inflate()
    • Copy input data into sandbox
    • Set addresses in z_stream
    • Call sandboxed inflate()
    • Copy output data out
  • malloc() with no-op free()

12 of 16

13 of 16

MinSFI: Sources of overhead

  • Copying data into/out of sandbox?
    • zlib interface: uses memory allocated by caller
    • ~2%
  • Costs of:
    • bounding address to 32 bits? (high)
    • adding sandbox base address? (low)
  • LLVM: quality of generated code (vs. GCC)
    • ~10%
  • Loss of addressing modes
    • e.g. Array access: “movl (%rax, %rcx, 4), %edx”

14 of 16

MinSFI: To-do list

  • Extend to x86-32
  • Implement indirect function calls
  • Vet LLVM IR further
    • Disallow “unreachable”
    • Whitelist external functions
  • Add bounds check to memcpy()
  • Allow multiple sandbox instances
  • Allow multiple threads
  • Sandbox teardown/recycling

15 of 16

Possible uses

  • Decoders: png (uses zlib), jpeg, ffmpeg
  • Sqlite: exposed to web
  • Crypto: NSS
  • libxml, xslt: proxy XML nodes?
  • Blink
    • V8 can stay outside sandbox
    • Site isolation, in-process
  • <Add scary library here>

What’s easy to proxy?

What overheads are acceptable?

16 of 16

Source code