WebAssembly / WASI

WebAssembly System Interface
Other
4.84k stars 251 forks source link

EFAULT or trap for out of range memory access during a wasi call? #505

Open yamt opened 1 year ago

yamt commented 1 year ago

it seems that runtimes disagree on how to handle out of range access. a quick example: https://github.com/yamt/toywasm/blob/master/wat/wasi/efault.wat

is there any authoritative text to require either behaviors or to allow both of them? i guess EFAULT is a natural choice if you consider wasi an analogous of system calls.

sunfishcode commented 1 year ago

In the component-model, Canonical ABI, it has traps like trap_if(ptr + length * size(elem_type) > len(cx.opts.memory)) for places where pointers are passed in. There isn't any documentation of this for preview1 yet, but I propose it should also trap.

EFAULT is the conventional Unix syscall thing to do. But Unix does this because it's unable to deliver a SIGSEGV for a trap that happened in the kernel. A signal would require the ability to save and restore the kernel stack, which would add a lot of complexity, given how Unix is designed.

In WASI, there is no fundamental difference between "syscall" and 'library call". Even things which are modeled after Unix syscalls, like wasi-filesystem, could just as well be implemented as library calls to other wasm code. If you pass an out-of-bounds pointer to a library to dereference, it would typically be expected to trap.

This ability to virtualize APIs and treat calls to wasm and calls to the host as interchangeable is one of the unique strengths of Wasm. Passing an out-of-bounds address to most APIs means the application has a bug, and it's the kind of bug that is strongly correlated with memory corruption, and it's better to trap the program than to give it an error code to handle and hope for the best.

yamt commented 1 year ago

In the component-model, Canonical ABI, it has traps like trap_if(ptr + length * size(elem_type) > len(cx.opts.memory)) for places where pointers are passed in. There isn't any documentation of this for preview1 yet, but I propose it should also trap.

ok.

EFAULT is the conventional Unix syscall thing to do. But Unix does this because it's unable to deliver a SIGSEGV for a trap that happened in the kernel. A signal would require the ability to save and restore the kernel stack, which would add a lot of complexity, given how Unix is designed.

In WASI, there is no fundamental difference between "syscall" and 'library call". Even things which are modeled after Unix syscalls, like wasi-filesystem, could just as well be implemented as library calls to other wasm code. If you pass an out-of-bounds pointer to a library to dereference, it would typically be expected to trap.

This ability to virtualize APIs and treat calls to wasm and calls to the host as interchangeable is one of the unique strengths of Wasm. Passing an out-of-bounds address to most APIs means the application has a bug, and it's the kind of bug that is strongly correlated with memory corruption, and it's better to trap the program than to give it an error code to handle and hope for the best.

is it really interchangeable? i thought a host implementation was somehow assumed.

my understanding is that, to use wasi, a module needs to:

can wasm have a way to handle this kind of circular dependencies among modules?

sunfishcode commented 1 year ago

It is somewhat awkward to do with preview1 binaries, but it is doable. For example, we're taking advantage of this very property in the preview1-to-preview2 polyfill we're building here, which implements the preview1 API with wasm implementations which call preview2 APIs.

yamt commented 1 year ago

It is somewhat awkward to do with preview1 binaries, but it is doable. For example, we're taking advantage of this very property in the preview1-to-preview2 polyfill we're building here, which implements the preview1 API with wasm implementations which call preview2 APIs.

interesting. i will probably take a look. thank you.

yamt commented 1 year ago

It is somewhat awkward to do with preview1 binaries, but it is doable. For example, we're taking advantage of this very property in the preview1-to-preview2 polyfill we're building here, which implements the preview1 API with wasm implementations which call preview2 APIs.

interesting. i will probably take a look. thank you.

i took a look and ended up with a simpler demonstration without component-model: https://github.com/yamt/toywasm/tree/master/examples/wasi_hook i think i now understand what you meant by "somewhat awkward"...