It would be interesting to see how this is designed for security in mind.
It would be interesting to see how this is designed for security in mind.
Struggling to see it.
1. Control flow is always checked. You can't jump to an arbitrary address, you jump to index N in a control flow table.
2. Calls out of the sandbox are also table based.
3. Indexed accesses are bounds checked. On 64 bit platforms, this is achieved by demoting the wasm to 32 bit and using big guard pages. On 32 bit platforms, it's explicit compares.
The result is something which may become internally inconsistent (can Heartbleed) but cannot perform arbitrary accesses to host memory.
JVM and WASM both have statically-verifiable control flow. No wild jumps, no executable stacks, etc. Phew.
Arrays and pointer arithmetic are a big difference. WASM has a big linear memory block, and instructions may access arbitrary locations within it - the runtime performs bounds checking only at the edges. So your `sprintf` can still overflow the buffer, and smash the stack's data, but can't affect the host, or the control flow.
JVM goes further: it prohibits pointer arithmetic and pushes array accesses down into the instruction stream. To access a JVM array, you must provide the array reference itself, and the runtime will perform bounds checking using the length.
The JVM approach gives you better runtime safety - no Heartbleed! The WASM approach is lower-level and is more easily adapted to existing system languages: C++, Rust, other LLVMites.
And while WASM trumps the security trumpet, without actually supporting proper bounds checking, the CLR will taint C++ pointer arithmetic as unsafe, thus making the whole module unsafe.
So I as consumer can decide if I am willing to trust an unsafe module or not.
Which is something that WASM isn't being honest about, corruption of internal data structures is allowed.
If I can control what goes into memory just by calling module public functions with the right data set and access patterns, CFI won't help a thing.
Suddenly the authorization module that would authenticate me as regular user, might give me another set of capabilities and off to the races.
/clr:pure uses unsafe and supports those cases though.
And yeah, WebAssembly only doing bounds checking within a single memory block and not actually offering true bounds checking is a big downgrade, and a pretty much unjustified one (+ it's rare among JITted languages...).
Consider: should the VM somehow try to analyze the running code and determine when it's about to commit a logical error and return "forbidden" data? What specification states which data is forbidden?
Consider: even the most high-level VM can execute a program with logical errors if it is Turing-complete. A Java or Erlang program running on a bug-free, fully-compliant implementation of the respective VM can still get (e.g.) an authorization condition wrong, or incorrectly implement a security protocol, or return data from the wrong (in-bounds, but wrong!) index in an array.
Secondly, it could have been designed with bounds checking for any kind of data access.
The CLR is honest about it, when C++/CLI uses C style low level tricks, the Assembly is considered tainted and requires explicit allowance of unsafe assemblies execution.
An idea that goes back to Burroughs B5000, where tainted binaries (code using unsafe code blocks) requires admin permission for execution.
Needs to be memory safe otherwise a wasm program can execute arbitrary code, access memory that it should not, etc.