6 ms·
WebAssembly 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
by throw10920 5y ago
WebAssembly 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.
- IshKebab 5y agoSort of. The main goal for WebAssembly is to be a fast platform-agnostic compilation target that you can use in websites. The "in websites" bit means that it has to be completely safe (i.e. no accessing outside memory etc.) but it's not the main goal. This is a bit of a hack because Mozilla don't care about the platform-agnostic bit, so they're taking LLVM IR, compiling it to WebAssembly, then back to LLVM IR just so that they can ensure that the code is safe. WebAssembly comes with extra constraints that you probably don't care about if you're compiling to native (e.g. there's no 'goto') so you would get more efficient code (and probably faster compilation) if you just had some way of compiling LLVM IR directly to "safe binary". That would probably be a mountain of work though so its understandable why they went with this. Would be nice if they said how much the performance was impacted.
- deleted 5y ago[deleted]
- formerly_proven 5y agoIn 2021, the world finally achieves AS/400 on the web.
- wffurr 5y agoCan you expand on that? Was this a property of C compilers or other languages on IBM mainframes? I get that it's tongue in cheek, but it would probably be even funnier / ironic if I had more context to understand it. It's the same spirit as languages adopting functional programming techniques aka rediscovering Lisp.
- formerly_proven 5y agoAS/400 had a machine-independent binary format which was translated ahead of execution by the system's specific compiler into machine code, and all applications ran in the same address space with zero memory protection because the code generated by the compiler ensured isolation.
- ksec 5y agoOh wow. So again, we have come a full circle?
- 1_player 5y agoIncredible, I always thought that was an idea worth exploring. AOT compilation and not paying the cost of context and MMU switch would unlock incredible performance boosts, even on modern processors. I didn't know AS/400 did that already. IIRC Microsoft explored something similar with their research OS Singularity.
- tambourine_man 5y ago>because the code generated by the compiler ensured isolation That's fascinating. How does it do so? I thought it only separated the address space into user space and OS space.
- 5y ago
- madflame991 5y ago> 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 I saw a talk a while ago that was advocating for the same thing, except this was about JS and not webassembly. I can't find it tho - I remember it being related to the WAT js talk; It also mentioned that it would eliminate rings on the cpu (and simplify cpus) and context switches which would make execution faster; they were citing some MS research on the matter - damn I really wanna find the talk now... Edit: https://www.destroyallsoftware.com/talks/the-birth-and-death-of-javascript https://www.destroyallsoftware.com/talks/the-birth-and-death... thanks BoppreH MS research: "Hardware-based isolation incurs nontrivial performance costs (up to 25-33%) and complicates system implementations" (virtual memory and protection rings); I think MS knows what they're talking about here
- throw10920 5y agoThanks for the link. I would argue that a true trusted compiler needs to accept an unmanaged language and emit code without a runtime, though. A runtime is cheating, because you can always make one that implements an iron-clad sandbox that doesn't require processes...by implementing a (very slow) VM. To put in another way - I don't think that security or performance are that hard to achieve on their own - the hard part is getting both at once. And then, adding expressiveness on top is even more difficult, as Rust as aptly demonstrated.
- kaba0 5y agoRust is not secure at all in the sense used here — untrusted, arbitrary user code written in rust is a security threat.
- kmeisthax 5y agoMore specifically, unsafe blocks may violate the compiler's security guarantees and procedural macros actually run inside the compiler process at build time. Declarative macros do this too, but they're far too restricted to allow shenanigans. Procmacros can disable Rust's stability guarantees[0]. [0] https://github.com/m-ou-se/nightly-crimes https://github.com/m-ou-se/nightly-crimes
- zozbot234 5y agoWe will always need process boundaries to separate information domains, because side-channel vulnerabilities are not addressed by having a "trusted" compile step.
- throw10920 5y agoWhen it comes to side-channel attacks, process boundaries aren't adequate either. Rowhammer, Meltdown/Spectre, and friends show that handily, and the RSA key leakage attack using SDR shows that even machine isolation isn't going to be enough for some things. I guess that the idea that trusted compilers are the way forward is predicated on the assumption that we've managed to mitigate most/all side-channel attacks, because there really isn't much you can do about those otherwise.
- naasking 5y agoThose side channels existed because because they very clearly violated process isolation. Of course processes aren't going to isolate if your hardware ignores your isolation boundaries. Enforcing this kind of isolation in software will likely be quite difficult though.
- lisper 5y agoWhy can't a trusted compiler prevent side-channel attacks? All you need to do is prevent the code from accessing the side channel. It seems to me that doing this at compile time would actually be easier than doing it at run time.
- sfink 5y agoIt is easy. You just have to forbid access to timers and loops. Where "timers" include anything that can count and store the count in shared memory. Alternatively, you could forbid branches (and therefore loops, implicitly).
- lisper 5y agoI presume that was meant to be facetious? But a clock is not the thing you would prevent access to. The thing you would prevent access to is whatever it is you want to try to time using that clock.
- titzer 5y ago> it's a trusted compiler Sorry I have to quibble here, but this term is already a thing, and typically has the opposite connotation: a compiler that must be trusted because we cannot verify the output. It's trusted because we "trust" it (to not screw up). I would argue that this makes the compiler untrusted--we don't care what it does, whatever it outputs is going to be both statically and dynamically verified to not break the sandbox properties. > 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. I thought this right up until, well, about this time in 2017. Side-channel attacks are a real and bad thing. Our conclusion is that Spectre, in all its flavors, break confidentiality for in-process memory, regardless of sandboxing technology. On current hardware, there is no 100% bulletproof way to enforce isolation.
- girvo 5y ago"Trustable" compiler, perhaps?
- r00fus 5y agoTrustworthy Compiler is the term we're looking for (analogous to trustworthy computing vs. trusted computing aka JANUS/Intel ME/etc)
- bestouff 5y agoWell yeah isn't the same thing then ? The resulting code is plain x86 assembly, and you just trust it to have been sanitized by its transformation to/from WASM. Looks like your definition of a trusted compiler to me.
- josephg 5y agoYeah, it sounds like this is a clever hack to get clang to compile C code into a safe binary form; with runtime validation around pointers and array lookups and things. I'm surprised clang doesn't simply have a mode to do this already. Its weird we need this strange wasm dance. I'm imagining a sort of a release mode version of ASAN + UBSAN where the compiled code has bounds checks for all array and pointer lookups. (And probably some more checks, besides). The rule is that it should be impossible for any binary code compiled this way to overrun arrays, or read or write memory outside of its allocated section. No matter the C code, the binary is memory-safe. It would have a (modest) cost in runtime performance and binary size, but frankly that would be a trade off well worth making in all sorts of software we use daily.
- wahern 5y ago> I'm surprised clang doesn't simply have a mode to do this already. Its weird we need this strange wasm dance Compiling to WebAssembly doesn't make the code safe; it simply compiles it to a virtual machine with a memory model that isolates any bugs from the rest of the parent process (presuming a bug-free parent process). Basically, all pointers in WebAssembly are indexes to a global array (or global arrays--one for data and another for functions). It's basically the same trick people use in Rust to get around restrictions on reference cycles. And like in Rust, the compiled code can still have the same dangling pointer and buffer overflow problems, but the memory thus referenced is restricted to the unstructured array, rather than to potentially any address in the parent process' address space. The difference between Rust and WASM is that when you compile WASM to native code, at least when JIT'ing the code, you can completely elide the extra degree of memory reference indirection. (I don't think the extra indirection can be elided for AOT compilation of WASM code without a special runtime linking step.) The cost, however, is that you're left with this single global data array, so any bug in the WASM program can break any other part of the WASM program, whereas in Rust you effectively end up with many segments of unstructured memory (basically, one per component that uses the trick) that can't be cross-referenced if the program is buggy.
- jcranmer 5y agoThere's a very good reason such a thing doesn't exist: any C compiler that worked like that would break the vast majority of C programs. Fundamentally, memory safety requires that you keep track of not just where a pointer currently points to, but where a pointer could validly point to. But, as applied in practice, the C ABI requires that pointers be integers of where it currently points to; making pointers also include range information (aka "fat" pointers) is possible, but requires rewriting almost any software you care about--it's the kind of change that's about as fundamental to C programs as assuming 2's complement arithmetic. The way tools like ASAN and Valgrind work is by instead keeping track of where any pointer could point to (via shadow maps, basically a bit per memory location saying if you can read/write that location), and then padding every allocation with sufficient space that an innocuous read overflow (especially one-past-the-end) is likely to hit the padding. Their guarantees are at best probabilistic, not anything you would want to rely on for security.