Open tomaka opened 3 years ago
There are two possible attack vectors that I can think of that we should somehow defend against:
Deliberate data races in message body. A receiver might for example naively first reads some part of the message checks some permissions, then read the message again to actually execute it. It is possible that in the meanwhile the sender has modified the body of the message (even though it wouldn't be allowed to do). While a good implementation of the receiving side can erase this threat, I feel like there should instead be a guarantee from the system that message bodies are not modified while being processed.
Side channel attacks. If for example the receiving side does write_first_half_of_response(); foo(); write_second_half_of_response();
, a malicious program could repeatedly read the buffer containing the response to determine how long it takes to do foo()
. Similar to above, it would be a good idea to forbid reading responses until they've finished being written.
At the time of writing this issue, the WebAssembly threads proposal is still a work in progress. The Rust compiler has some support for
atomic_notify
andatomic_wait
, but it requires compiling the standard library with a specific flag. Thewasmi
interpreter doesn't support these instructions.However, let's assume that everything is ready and
notify
/wait
are supported, and memory sharing is possible.A message sender and its receiver should actually share memory in order to write messages directly to the target without copying said messages.
The details of sharing memory are still a bit uncertain, and it is unclear to me whether it will be possible to add memories at runtime.
Whether it is the case or not, each interface registered or used should result in one memory object being attached to the VM. This memory object would be shared with the other side of the interface, and contains the messages being sent and the responses. In order to avoid collisions in the memory allocators, the sender must allocate the buffer containing the responses.
Contrary to what might be the intuitive thing to do, we shouldn't make programs send messages to each other directly, as the kernel is responsible for multiplexing the messages and ensuring a fair distribution.
A ring buffer would be used in order to send messages, and another one for receiving responses. These two ring buffers are not tied to a specific interface, and thus can be in any memory (most likely the main one). Sending a message would be done by writing the message in the appropriate memory, adding an element in the ring buffer, and doing
atomic_notify
to notify the kernel of the message. Waiting for a response would be done withatomic_wait
to wait for the receiving ring buffer to contain elements. Sending a messages needs to indicate the location of the ring buffer that will receive the response.One unresolved question is how to rework the interface message requests to fit this scheme.