4 ms·
Parent comment refers to running buggy code in ring 0, however in a design like wasmjit, that code would be compiled first to wasm, and the actual code running
by _wmd 8y ago
Parent comment refers to running buggy code in ring 0, however in a design like wasmjit, that code would be compiled first to wasm, and the actual code running in-kernel is a derivation that includes bounds checking on at least memory operations. The original code never "runs" in ring 0, what runs is a derivation that includes a software equivalent of the hardware isolation we're used to and have relied on for the past 20 years.
While I haven't studied wasmjit, the most obvious implementation is to run system calls naturally, with a global "struct task" existing as before that defines the semantics of the current context, including details like the current UID and capability mask - in other words, without effort, read() and open() could be made to behave identically to before, it's just that the caller now lives in a software sandbox rather than a hardware sandbox, and most/all expensive hardware reconfiguration was avoided
- comesee 8y agoI read through it and this is indeed how it works. Wasm as a language provides the restrictions that the MMU provides for machine code. You can't read from/write to arbitrary pointers in wasm.
- pjmlp 8y agoWASM does not do bounds checking on pointer data.
- comesee 8y agoIt does
- pjmlp 8y agoIt does not, something like char buff[100]; char func(int idx) { char *ptr = buff; return ptr[idx]; } int main (void) { printf("%c", func(200)); return 0; } Compiles nicely to something like this (module (type $FUNCSIG$ii (func (param i32) (result i32))) (import "env" "putchar" (func $putchar (param i32) (result i32))) (table 0 anyfunc) (memory $0 1) (export "memory" (memory $0)) (export "func" (func $func)) (export "main" (func $main)) (func $func (; 1 ;) (param $0 i32) (result i32) (i32.load8_s (i32.add (get_local $0) (i32.const 16) ) ) ) (func $main (; 2 ;) (result i32) (drop (call $putchar (i32.load8_s offset=216 (i32.const 0) ) ) ) (i32.const 0) ) ) Which will gladly blow up, or not, when i32.load8_s gets called. https://webassembly.studio/ https://webassembly.studio/
- comesee 8y agoThe load8_s instruction will check that the computed offset into the memory of size 64KB (1 wasm page) does not index past the bounds of 64KB. If it does, the program will trap.
- pjmlp 8y agoWhich will not work on the provided example, thus leading to memory corruption. Where is the WebAssembly implementation that traps on my example?
- comesee 8y agoWebAssembly protects the host from memory corruption by the user module. To do this it does a bounds check before executing the load. It does not protect a user module from itself. It does not change the semantics of C. Relevant documentation http://webassembly.github.io/spec/core/syntax/instructions.html#syntax-instr-memory http://webassembly.github.io/spec/core/syntax/instructions.h...
- pjmlp 8y ago> It does not protect a user module from itself. It does not change the semantics of C. Which is my whole point, WebAssembly does not protect memory corruption inside of the module code, which allows for security exploits anyway. On my sample code if I expose func() to the host, and it gets called with 200 as parameter for a buffer size of 100 bytes, no trap will ocurr. On a real use case that call might induce an internal memory corruption that will, for example, change the behavior of other functions exposed to the host. If you wish I can provide an example how to do that, which you can try out in your favorite spec compliant Web Assembly implementation.
- TomMarius 8y agoCurrent WASM spec is a MVP. There will be other ways to expose functionality to the host that should be safer. See future proposals. Your points are valid though.