11 ms·
WebAssembly and Back Again: Fine-Grained Sandboxing in Firefox 95
- Jyaif 5y agoThe downside to this technique is that wasm2c code is 50% slower, so (at least for now) process-isolation is still a win in some cases (when the overhead of process-isolation is small compared to the rest). Still, that's a very exciting development that could lead to a revolution in operating systems.
- floatboth 5y ago42% slower only in the worst case using the slowest (explicit) bounds checks, only 12% slower with a signal handler: https://kripken.github.io/blog/wasm/2020/07/27/wasmboxc.html https://kripken.github.io/blog/wasm/2020/07/27/wasmboxc.html
- paulgdp 5y agoHow is it different from using Clang's CFI (control flow integrity)? I thought this was the same technique used in webassembly. Chromium is using this too i think
- azakai 5y agoCFI helps with control flow exploits, but it doesn't prevent memory corruption for example. This sandboxing technique ensures that both control flow and memory accesses remain in the sandbox (except for when you explicitly allow otherwise).
- Deukhoofd 5y agoSo if I'm understanding this correctly, Firefox compiles its dependencies as WASM, effectively blocking function calls to things it shouldn't and illegal memory access, and then translates it back to C so it can compile it normally? Sounds neat!
- fyrn- 5y agoNot back into C, from WASM to native executable / asm / object code
- bholley 5y agoDeukhoofd is correct — we compile the WASM code back into C in order to reuse and reduce friction with our existing compilation pipeline.
- wfh 5y agoHow does this compare with https://chromium-review.googlesource.com/c/chromium/src/+/2775240 https://chromium-review.googlesource.com/c/chromium/src/+/27... ?
- Hendrikto 5y agoThat was only for the prototype, the current implementation is: source code -> WASM -> C -> native code
- flohofwoe 5y agoThe solution described in the post actually translates C/C++ to WASM, and then translates the WASM bytecode back to C (via a tool called wasm2c), which is then fed back into the C compiler again to compile to native code, all 'offline' in the Firefox build process.
- makeworld 5y agoI don't have a lot of knowledge in this area, but using WASM for forcing code to be safe seems bizarre. Why aren't there just compiler flags that can enforce the same restrictions they want?
- gostsamo 5y agoThis looks more like using the compilation to wasm and back to automatically rewrite the code of entire components in a manner that makes them safer.
- 7373737373 5y agoWhy is it bizarre? The Wasm function interface seems perfect for sandboxing code. It's a "whitelist" system, the contained process can only call external functions that have been explicitly attached, perfect for implementing the capability security paradigm and progressively hollowing out the attack surface by separating functionality into several instances.
- Deukhoofd 5y agoRequiring compilation to WASM to then translate it back to C and compile it again might be a bit strange. Clang obviously already has the tools to do the WASM sanitizing, it might be really cool to have a way to directly enforce those rules outside of WASM.
- bilkow 5y agoAs I understand it, clang doesn't have the tools to sanitize WASM. It just emits WASM, which, malicious or benevolent, can't access memory outside its designated memory regions. It's wasm2c job to ensure that the C generated enforces the WASM memory rules, so I'd say the one sanitizing the code is not clang but wasm2c.
- azakai 5y agoFor technical reasons adding compiler flags to do that is fairly hard. You'd need to handle a lot of things like compiling to the sandboxed format, system library support, the FFI to normal code, etc. It would be possible to do all that, but wasm has already done it - so compiling to wasm as an intermediary step is the most practical solution. (See also https://news.ycombinator.com/item?id=29460766 https://news.ycombinator.com/item?id=29460766)
- vlovich123 5y agoI can’t find it right now but I read a paper that showed that the WASM security model is weaker than native compiled code in some cases. For example, due to compiler and OS hardening techniques, exploits of a libpng flaw weren’t exploitable unless run in WASM. You couldn’t escape the WASM sandbox but the a application itself could be compromised. I’m sure that this approach is valid as a hardening measure but some of the enthusiasm in the post is perhaps worthy of temperance. This thunk through WASM can’t protect against runtime heap overflows and such. > However, the transformation places two key restrictions on the target code: it can’t jump to unexpected parts of the rest of the program, and it can’t access memory outside of a specified region Oof. The paper I recall specifically called these out as not enforceable. The libpng example in the paper directly had an external request have libpng corrupt and access other WASM memory than it owned (in this model it would be other in-process native memory I think or at least the other code placed within the same heap region unless each component gets its own which then means you need to have a fixed memory allocated upfront…).
- Deukhoofd 5y ago> WASM security model is weaker than native compiled code in some cases. For example, due to compiler and OS hardening techniques, exploits of a libpng flaw weren’t exploitable unless run in WASM. If I understand the post correctly, it's still native compiled code in the end, and it won't run through WASM. The goal of the approach sounds more like a code sanitizer tool to ensure the external library they're using isn't making calls outside of it, or requesting memory beyond the region its given.
- miloignis 5y agoAre you perhaps thinking of "Everything Old is New Again: Binary Security of WebAssembly"? ( https://www.usenix.org/system/files/sec20-lehmann.pdf https://www.usenix.org/system/files/sec20-lehmann.pdf ) In any case, I think you've misunderstood the security properties. WASM can have weaker security within the sandbox because it doesn't have access to some of the more sophisticated mitigation measures that native code does, but the security of the sandbox boundary itself is very solid. The part of the article that you quote is accurate in the sense that I believe it was meant - the code cannot jump to unexpected parts of the rest of the program (outside the sandbox) and cannot access memory outside of a specified region (the sandboxed memory). A vulnerability might allow the target code to jump to somewhat unexpected parts inside the sandbox, or buffer overflows inside the sandbox, but not outside. As such, it's actually a really effective application of a WASM sandbox!
- mfrw 5y agoThis sandboxing is achieved via RLBox[0], which is a toolkit for sandboxing third-party libraries. It comprises of a WASM sandbox and an API which existing application can leverage. The research paper[1]. [0]: https://plsyssec.github.io/rlbox_sandboxing_api/sphinx/ https://plsyssec.github.io/rlbox_sandboxing_api/sphinx/ [1]: https://arxiv.org/abs/2003.00572 https://arxiv.org/abs/2003.00572
- chakkepolja 5y agoI am probably missing something: Why is WASM required here? Can't these analysis done directly on LLVM IR?
- ink404 5y agoIt could be, seems like they already had something in WASM for doing this so it made more sense to use that than re do on LLVM IR.
- fooyc 5y agoThat's what I though too. C compilers should be able to achieve that directly, and it's incredible that nobody though of doing so yet. What's great, though, is that they are achieving this with tools that are already available.
- azakai 5y agoYou're right that this could be done on LLVM IR. The MinSFI project did exactly that basically, several years ago, but it did not see adoption sadly. The benefits of wasm over LLVM IR is that wasm has already done the work to define the sandboxed format and build the tooling to compile to it. Wasm is also almost as fast as running normally. (Wasm is also portable and lacks undefined behavior, although for this use case those might matter less.) See the MinSFI section here which compares it directly to wasm for sandboxing: https://kripken.github.io/blog/wasm/2020/07/27/wasmboxc.html https://kripken.github.io/blog/wasm/2020/07/27/wasmboxc.html And the original MinSFI presentation is here: https://docs.google.com/presentation/d/1RD3bxsBfTZOIfrlq7HzGMsygPHgb61A1eTdelIYOurs/htmlpresent https://docs.google.com/presentation/d/1RD3bxsBfTZOIfrlq7HzG...
- glandium 5y agoLLVM IR is still CPU dependent, and is a moving target. WASM is also a moving target, but much more controlled.
- sdze 5y agoWhat a stupid idea to run bytecode in a browser. We have gaping security holes with JavaScript already. Stop the madness.
- Jyaif 5y agoIt's compiling the webasm back to C, so it's not running bytecode.
- Cloudef 5y agoWhile your comment is off-topic to this discussion. I find it funny that you think running bytecode in browser is worse idea than the invention of javascript.
- sdze 5y agohttps://www.infoq.com/news/2019/10/WebAssembly-wasm-malicious-usage/ https://www.infoq.com/news/2019/10/WebAssembly-wasm-maliciou...
- BoppreH 5y agoGetting strong vibes of The Birth and Death of JavaScript (2014) [1], one of the numerous great talks by Gary Bernhardt. My engineer side is happy seeing how strong tooling enables such creative features with high assurances. My futurist side is dreading the day Intel launches their first Javascript/WebAssembly-only processor. [1] https://www.destroyallsoftware.com/talks/the-birth-and-death-of-javascript https://www.destroyallsoftware.com/talks/the-birth-and-death...
- MangoCoffee 5y agoi don't think JavaScript going to die but its time that we have another option for the web. JavaScript have its warts. some people love JavaScript and some don't. its not fair for JavaScript to be the only option. i see Web Assembly as an option for people who doesn't like JavaScript warts to use their favor langue to develop for the web.
- Omnius 5y agoI feel like the only people that "like" javascript or those that had it for their first language. Its needed, its better than it was, but compared to just about any other language its a total mess.
- k__ 5y agoMy career was C, Java, PHP, JavaScript. I like JS the most. It's flexible, lightweight, and omnipresent. The only other mainstream language that gives me that feeling is Rust.
- allisfalafel 5y agoRust is hardly omnipresent. I understand it has trouble with lesser used architectures and operating systems. While yes you probably dont use them, they do still exist.
- chaostheory 5y agoThe ECMA improvements are nice but imo it's too hard to manage large Javascript programs, or Typescript wouldn't have such a large following.
- kevincox 5y agoThis is a really powerful tool and I hope we see this used more. Traditional process based sandboxing is very efficient inside the process, but IPC is very expensive. This approach flips the tradeoffs exactly backwards as the sandboxed code is slower, but IPC is nearly free. This means that it can cover exactly the space that was too expensive to sandbox before. The two approaches are perfect compliments for each other. I now imagine that the vast majority of code can be put into one of these two groups leaving very little code that is unable to be sandboxed for performance reasons.
- anonymousDan 5y agoWhat's the performance overhead in comparison to the unsandboxed version I wonder?
- azakai 5y agoThere are detailed performance numbers here on a variety of real-world codebases: https://kripken.github.io/blog/wasm/2020/07/27/wasmboxc.html https://kripken.github.io/blog/wasm/2020/07/27/wasmboxc.html tl;dr Something like ~14% when using the best bounds checking strategy, or ~42% when using the most portable one. (There are options in the middle as well.)
- bholley 5y agoIt varies. Here's an example of some performance analysis I did on the expat port: https://bugzilla.mozilla.org/show_bug.cgi?id=1688452#c37 https://bugzilla.mozilla.org/show_bug.cgi?id=1688452#c37
- Jyaif 5y ago> it can’t access memory outside of a specified region How are segmentation faults handled?
- azakai 5y agoWasm is defined to trap when it accesses memory outside the sandbox (the embedder can decide how to handle that trap, say by shutting down that particular sandbox). With wasm2c the trapping can be implemented in a variety of ways, for example using the signal handler trick like wasm VMs do (~14% overhead) or manual bounds checks (~42% overhead, but fully portable).
- bholley 5y agoI believe the implementation in Firefox masks off the high bits of pointers and adds the result to the base address before performing a load/store. This requires us to reserve a power-of-two-sized region of address space, but we can lazily/incrementally commit the pages as the sandboxed code invokes sbrk.
- azakai 5y agoThanks for the details bholley! Do you plan to use the signal handler trick eventually? Less portable but in my tests it shrinks the total overhead by half (from masking's 29% to 14%).
- bholley 5y agoSorry, I should have been more clear. I believe we use the masking on 32-bit platforms, which is faster than explicit bounds checks. On 64-bit platforms we use guard pages. We don't actually need a signal handler, because we don't need to gracefully recover from a fault like we do on the Web — we can just crash.
- azakai 5y agoNice, yeah, just crashing is even simpler, and sounds good enough...
- pjmlp 5y agoWith the recompilation back to C I fail to see how RLbox prevents memory corruption due to lack of bounds checking or UB being explored by the optimizer.
- azakai 5y agoThere is that risk, yes. For wasm2c to be correct it must emit C code without undefined behavior. As best we know it does that properly today, and we've tested and fuzzed it quite a lot, but whenever you use an optimizing C compiler on the output there is no 100% guarantee.
- Tobu 5y agoThe host application is still C, and the final compilation of the mix of C and sanitized C still relies on the optimizer to be fast, so it might be possible for the untrusted library to reveal UB in the rest of the application. I can see how the whole approach (wasm + taint) would make compromise of the sanitized bits through crafted data harder, but I'm not sure it does enough for guarding against a supply chain compromise of the library.
- Tobu 5y agoAnd I see the safe languages section[1] in the WasmBoxC post; that looks like it would address most of the remaining UB risk, but Firefox will take some time getting there. [1]: https://kripken.github.io/blog/wasm/2020/07/27/wasmboxc.html#safe-languages https://kripken.github.io/blog/wasm/2020/07/27/wasmboxc.html...
- SAI_Peregrinus 5y agoThere's no guarantee even without optimization. Some optimizations exploit UB, but not all "unexpected" code generation in the presence of UB is due to optimization. Take signed int overflow on addition. On some platforms ADD wraps, on others it traps. Depending on target CPU you'll get very different behavior even for non-optimizing compilers that just emit an ADD instruction! WASM is just another target, with its own behavior.
- davidkunz 5y agoCan't wait for Firefox XP.
- jerheinze 5y ago> Cross-platform sandboxing for Graphite, Hunspell, and Ogg is shipping in Firefox 95, while Expat and Woff2 will ship in Firefox 96. I wonder what the other "good candidates" that he referred to are.
- throw10920 5y agoWebAssembly is kind of a hack here (although a clever hack that saves a lot of effort) - the essence of what the Mozilla folks have done isn't WebAssembly, it's a trusted compiler - by which I mean a compiler that emits trustable code, regardless of how untrusted the source is. It's a really neat idea that I hope to see more adoption of, because our current security models for software suck. Security based on process isolation is extremely inefficient and coarse-grained - having a trusted compiler could (eventually) massively increase performance by removing processes entirely (no more virtual memory! no more TLB flushes and misses! less task switch overhead!) and eliminating the kernel/user mode separation, with an increase in security. "Could" because it's not clear to me if the reduction in expressiveness from our languages now to future languages with a theoretical trusted compiler (all jump targets have to be known at compile-time?) will be accepted by the majority of the populace. Look at how hard it is to get people to accept borrow-checkers...
- vanderZwan 5y ago> a compiler that emits trustable code, regardless of how untrusted the source is Hasn't this been one of the goals of the design of WebAssembly since day one, and something that has been getting people excited about it too? Using something for one of its intended purposes isn't really a "hack", no?
- oefrha 5y agoThe hack is translating wasm back to C, then compiling again. It wouldn’t be a hack if they’re running wasm, but they’re not.
- masklinn 5y agoAFAIK the ability to compile wasm to native code was pretty much always part of the goal, running wasm in a VM was never the end-game. Compiling via C might be considered a hack (especially in the sense that it introduces a potential weak link in the chain), but it makes a lot of sense since they want to integrate the result into the Firefox build artefacts, and compiling via C is not exactly novel either.
- kgeist 5y agoI wonder how it deals with intrinsics for optimization (SSE and the like), does it fail to compile, or maybe WASM has some support, or it's completely lost in translation?
- azakai 5y agoFor something like SSE to work you'd need both wasm and wasm2c to support it. Wasm doesn't support all of SSE, but wasm does have SIMD support which is a portable subset of common SIMD instructions. You may lose some performance there, but wasm is adding more instructions to help (see "relaxed-simd"). There are also headers to help translate between SSE and wasm SIMD for existing code where possible. wasm2c does have support for wasm SIMD, although I believe it is not 100% complete yet.
- _rdvw 5y agoFirefox appears to utilize a custom clang toolchain to enable this without documenting how to make such toolchain (wasi sysroot). And expects you to just download the precompiled version from their servers. Fedora and Fennec F-Droid have since disabled this feature. https://src.fedoraproject.org/rpms/firefox/c/4cb1381d80a94c9a41ded1a022276e1f664b1755 https://src.fedoraproject.org/rpms/firefox/c/4cb1381d80a94c9... https://gitlab.com/relan/fennecbuild/-/commit/12cdb51bb045c31741ae36094849b4d5ef91b5f3#2a3ffa9e598316176c1c8434bbf16a4aa511d325_244_244 https://gitlab.com/relan/fennecbuild/-/commit/12cdb51bb045c3...
- fabrice_d 5y agoPretty sure you can build it yourself from https://github.com/WebAssembly/wasi-libc https://github.com/WebAssembly/wasi-libc given that https://github.com/WebAssembly/wasi-libc/commit/ad5133410f66b93a2381db5b542aad5e0964db96 https://github.com/WebAssembly/wasi-libc/commit/ad5133410f66... is a contribution from a MoCo employee doing a lot of work around toolchains.
- floatboth 5y agoThere's also the https://github.com/WebAssembly/wasi-sdk https://github.com/WebAssembly/wasi-sdk repo which is kind of a meta-build-system for all this. But in FreeBSD we build all the pieces directly, here's our build recipes (with some hacks due to llvm's cmake code being stupid sometimes): compiler-rt (from llvm): https://github.com/freebsd/freebsd-ports/blob/main/devel/wasi-compiler-rt13/Makefile https://github.com/freebsd/freebsd-ports/blob/main/devel/was... libc (from what you linked): https://github.com/freebsd/freebsd-ports/blob/main/devel/wasi-compiler-rt13/Makefile https://github.com/freebsd/freebsd-ports/blob/main/devel/was... libc++ (from llvm): https://github.com/freebsd/freebsd-ports/blob/main/devel/wasi-compiler-rt13/Makefile https://github.com/freebsd/freebsd-ports/blob/main/devel/was...
- GekkePrutser 5y agoYou're responsible for making Firefox work on FreeBSD? Thank you! I really appreciate your work and I use it every day.
- rajanaccros 5y agoHow does this affect process isolation? If only some components can be sandboxed at this fine grained level, aren't we still subject to process isolation to sandbox everything else? It would seem like one still has to run fission.autostart true to isolate the components that cannot be compiled in this way, therefore not gaining the benefit of less overhead as stated in the article.
- bholley 5y agoThe purpose of RLBox is to add an extra layer of component-level isolation on top of Firefox's process-based site-level isolation. The reduced overhead is relative to the hypothetical scenario in which we performed the component-level isolation with processes (rather than WebAssembly).
- rajanaccros 5y agoOhh I see. Not a replacement for process based site level isolation. I just wasn't wrapping my head around that. Makes much more sense now. Thanks for the explanation.
- majkinetor 5y agoHowever, without removing processes it will still be as slow as today, which I really hope browsers will do.
- ekr____ 5y agoI think a more likely way to think about it is that this allows us to sandbox things that would otherwise would not be sandboxable. For a variety of reasons, it's probably not practical to remove the existing process sandboxes.
- FpUser 5y agoI am confused. Isn't WASM supposed to be eventually AOTed (Ahead of Time Compile) or a least JITed? Why this bizarre twist with WASM-C-NATIVE? Browser should do just that instead of these dances around.
- floatboth 5y agoThis isn't for running WASM from the web. This has nothing whatsoever to do with the WASM JIT that's in Spidermonkey. This is sandboxing for internal components of the browser (or any application really). But, this is a kind of AOT compilation involving WASM in the middle.
- jayd16 5y agoReminds me a bit of Apple's AOT protections together with Unity's IL2CPP approach.
- dmix 5y agoHere is an example of sandboxing a library and then calling functions: rlbox::rlbox_sandbox<rlbox_noop_sandbox> sandbox; sandbox.create_sandbox(); sandbox.invoke_sandbox_function(hello); https://github.com/PLSysSec/rlbox_sandboxing_api/blob/master/examples/hello-world-cmake/main.cpp https://github.com/PLSysSec/rlbox_sandboxing_api/blob/master... Seems like it could get a bit verbose when used all over the place but I guess there’s always a cost with security and having clearly defined risky parts also helps. Regardless I’m happy to see the effort being made beyond process isolation and OS capabilities.
- bshanks 5y agoI thought that in the wake of Spectre etc, the CPU vendors declared that they wouldn't support privacy guarantees within a process, and that only process sandboxing would be supported?
- wahern 5y ago> First, [hoisting a subcomponent into its own process] requires decoupling the code and making it asynchronous, which is usually time-consuming and may impose a performance cost. It may need to be asynchronous because of Firefox's peculiar infrastructure for process isolation, but if the intraprocess calls can be synchronous, the interprocess function calls could in principle also be synchronous. By default that's literally how most syscalls work, at least in Unix-like systems. And it's the direction microkernels seemed to have turned--synchronous calls work just fine, and you don't need a bunch of specialized asynchronous calling infrastructure inside the kernel itself. This seems like a classic case of using a novel solution to subtly redefine the problem. If you just redefined the problem at the outset you wouldn't need so much contrivance.
- thealig 5y agointeresting. Are there any resources to get in depth into browser security, specially for a relative novice? also what is the best way to contribute in the sub projects of Mozilla Firefox for example?