Fascinating! I did more research here, and here’s what I’ve found:
wasmer indeed uses wasm-level instrumentation, and not vm-level instrumentation, as I originally thought. That is, they inject a wasm global, and instrument the code to decrement this global in every basic block.
However, I believe that wasm spec guarantees that this instrumentation approach is safe and not abusable. The only way to wasm global variable from within wasm code is via global.set|get
instructions, which take the index of a global as a compile-time constant. That the constant denotes existing global of the correct type is checked at module validation time. Instrumentation adds a new global, after the module has been validated. Unlike functions, there’s no equivalent to call_indirect
instruction for accessing a global, determined at runtime.
I haven’t found an official super-direct statement claiming that this is safe, but:
- wasm security document mentions that locals and globals are accessible only via indexes
-
spec mentions that globals are accessible only via
global.get|set
instructions -
ctrl+f
in the instructions document doesn’t reveal any additional ways to access the global
So, this instrumentation is safe in principle, although I haven’t verified that its bug free.
The only hazard I see here (pointed out by @olonho) is that some future version of wasm will add instructions for indirect access to global. I don’t think this is a real problem: the fact that locals & globals are managed is a part of security model; indirect access is not necessary a problem – indirect acesses go via table, and it’s possible to verify that a specific entity is not escaped via a table; in the hypothetical case where instruction is added to lookup arbitrary global by raw index, we can refuse to run contacts with this particular insturction.