Webassembly As a Practical Path to Library Sandboxing
Total Page:16
File Type:pdf, Size:1020Kb
Gobi: WebAssembly as a Practical Path to Library Sandboxing Shravan Narayan Tal Garfinkel Sorin Lerner Hovav Shacham UC San Diego Stanford University UC San Diego UT Austin Deian Stefan UC San Diego Abstract 1 Introduction Software based fault isolation (SFI) is a powerful ap- proach to reducing the impact of security vulnerabilities Software-fault isolation (SFI), introduced in 1993 [29], is in large and critical C/C++ applications like Firefox and an effective technique for reducing trust in software com- Apache. Unfortunately, practical SFI tools have not been ponents in both userland and kernels [28]. But the dom- broadly available. inant mechanism for module isolation today is process- Developing an SFI toolchain is a significant engineer- based privilege separation and sandboxing [6,23], not SFI. ing challenge. Only in recent years have browser vendors Process-based isolation carries with it IPC overhead that invested in building production quality SFI tools NaCl/P- can be substantial for tightly coupled modules. In this NaCl to sandbox mobile code. Further, without committed paper, we argue that WebAssembly, developed for accel- support, these tools are not viable, e.g.,NaCl has recently erating Web content, is a viable basis for general-purpose been discontinued, orphaning projects that relied on it. SFI that is performant and maintainable. WebAssembly (Wasm) offers a promising solution—it Unlike process-based isolation, SFI does not enjoy can support high performance sandboxing—and has been instruction-set support on widely deployed processors. embraced by all major browser vendors—thus seems to Accordingly, SFI vendors need to develop and maintain have a viable future. However, Wasm presently only offers their own compiler backend (to rewrite modules to re- a solution for sandboxing mobile code. Providing SFI for strict memory accesses), their own linker and loader (to native application, such as C/C++ plugins or libraries install modules in the host address space), and their own requires additional steps. base library and ABI (to facilitate interaction between To reconcile the different worlds of Wasm on the the module and the outside world). This is a large un- browser and native platforms, we present Gobi. Gobi is a dertaking. Google’s Native Client (NaCl) SFI effort was system of compiler changes and runtime support that can largely rewritten in the port from 32-bit x86 [30] to 64- sandbox normal C/C++ libraries with Wasm—allowing bit x86 [26], and the most recent version of LLVM for them to be compiled and linked into native applications. which Google has supplied NaCl backend patches is 3.7.0, Gobi has been tested on libjpeg, libpng, and zlib. released in late 2015. Based on our experience developing Gobi we offer a WebAssembly (Wasm) [14] has supplanted NaCl for detailed analysis of how SFI developers can address the high-performance Web content sandboxing in browsers. difficult challenge of maintaining an SFI toolchain long It is a viable basis for general purpose SFI. Wasm al- term. We conclude with a call to arms to the Wasm com- ready provides the core SFI guarantee: isolated mod- munity and SFI research community to make Wasm based ules can access memory only within a single region. Un- module sandboxing a first class use case and describe how like NaCl, Wasm was designed for hosting isolated mod- this can significantly benefit both communities. ules in-process, rather than in a dedicated NaCl process. Addendum: This short paper was originally written in And Wasm’s popularity—it is implemented in all major January of 2019. Since then, the implementation and de- browsers [18]—means that it is supported by a robust and sign of Gobi evolved substantially and some of the issues growing toolchain ecosystem. raised in this paper have since been addressed by the We have built a prototype Wasm SFI system, called Wasm community. Some challenges still remain. We thus Gobi. Our experience highlights several shortcomings of left the paper largely intact and only provide a brief up- the current Wasm ecosystem for applications beyond Web date on the state of WebAssembly tooling as of November content acceleration. Some shortcomings are already be- 2019 in the last section. ing addressed by the community. For example, browsers 1 use their existing JIT infrastructure to compile Wasm Emscripten specific “embedding” runtime. This runtime bytecode to machine code. For host processes that don’t is providing support for C primitives and platform ABIs already include a JIT, a standalone machine code gen- such as stack and heap separation in the address space, erator is needed—and is currently being developed as primitives for setjmp and longjmp, threading primitives, Cranelift [8]. Others are not currently being addressed. floating point primitives and clock/time primitives. Em- For example, the C/C++ ABI implemented by Wasm com- scripten provides this runtime for Wasm as used on the pilers and browser hosts has not been standardized or even web, it does not provide this runtime for non-web embed- fully documented. We sound a call-to-action to the Wasm dings. We bridge this gap in Gobi. community to address these shortcomings. Syscalls. The libjpeg Wasm module needs to invoke Gobi is intentionally designed to minimize changes syscalls (e.g., for I/O or logging). However, Wasm only to the Wasm compiler, even at the cost of some perfor- supports a small number of memory related syscalls in mance. We are working to upstream our changes to the the Wasm runtime. Other syscalls are either unsupported Emscripten compiler. We describe the design decisions or unofficially supported in the Emscripten embedding we made to maximize the maintainability of Gobi within runtime. Gobi thus provide support for several syscalls, the Wasm ecosystem. ensuring compatibility while retaining memory isolation. Preliminary compatibility and performance evaluation Module sandboxing APIs. To safely use the sandboxed of Gobi supports the feasibility of Wasm as a base for libjpeg module the Gobi runtime also provides several general-purpose SFI. To date, Wasm performance has APIs to application code: been compared against JavaScript performance, not native code. Perhaps as a result, the bounds-checking techniques I Function Calls. The application calls into libjpeg to employed by current Wasm runtimes are well behind the parse the JPEG image. However, the application and SFI state of the art. We thus sound a call-to-action to the libjpeg Wasm module have ABI differences. We SFI researchers to adopt Wasm bytecode as an SFI in- provide an API that accounts for ABI differences (e.g., termediate representation and to bring high-performance function name mangling, struct parameters, etc.). sandboxing to Wasm. I Memory Allocation. The application and libjpeg must share data such as the parameters of function calls. To 2 Prototype allow controlled sharing, we provide an API that allo- cates memory in Wasm module memory. This memory Our positions in this paper stem from our experience build- is accessible to both the library and the application. ing Gobi. Therefore we briefly discuss the various require- I Pointer Swizzling. Pointers in the Wasm libjpeg mod- ments addressed by Gobi to leverage Wasm for module ule are to be represented in the format specified in sandboxing. Wasm standard—indexes into the Wasm memory. We By itself, Wasm provides an essential building block— thus provide APIs to convert or “swizzle” pointers to memory isolation, i.e. a module can only access its own the Wasm representation and its reverse. memory. However, we must safely use this memory iso- Callback Registration. The application may need the lated module in the application. In this section, we use I Wasm libjpeg module to invoke some application func- an example of an image decoder application that wishes tions. For example, libjpeg needs to invoke an error to use libjpeg (a JPEG decoding library) in a sandboxed handler application if it encounters a parsing error. We manner. To setup, we compile an unmodified libjpeg with thus provide an API to register callbacks. our custom Emscripten compiler to produce a Wasm mod- ule, which is in turn compiled with a Wasm compiler, such I Threading. To safely support multiple application as Cranelift, to produce an ELF library that is used with threads calling into libjpeg simultaneously, we must the Gobi runtime. create a new Wasm stack for each calling thread. We thus provide an API to create a new stack in the Wasm Runtime. To run Wasm modules like libjpeg, we should module which is called for each application thread. only require a simple standardized Wasm runtime for which multiple open source implementations exist [3, 16, Compiler modifications. For the application and Wasm 25]. This runtime provides basic functions for Wasm mod- libjpeg module to be compatible, they must have compati- ule setup—providing APIs to extend or free memory for ble machine models—identical sizes and alignments for the Wasm module, mechanisms to correctly handle traps, datatypes such as an int, long and the size of a pointer. implementation of data structures for indirect function In practice these are not the same: the application uses the call support (the Wasm specification requires all indirect default machine model of the target platform, however, the calls to go through the runtime to ensure memory safety). Wasm libjpeg module uses the machine model dictated In practice, the libjpeg Wasm module also requires the by the Wasm standard and toolchain. We resolve these 2 differences by modifying the machine model used by Em- and cannot be used for multi-threaded programs. We pro- scripten when compiling Wasm modules, taking care to pose an alternative approach that that minimizes compiler minimize the amount of changed cod (see Section 3.1). changes without giving up on performance.