There are ways to have a verifier check to make sure that this kind of code modification doesn't happen. It's a very hard problem, though, and maybe not suitable for things like Javascript.
What I'm referring to is not code modification, but heap corruption. The simplest example would be that if the to: and bcc: list for your email live in a wasm heap, any bug in that wasm application could be used to redirect your email. The same is true for JavaScript, C#, etc but the key difference is that those are memory-safe languages under normal circumstances, while WASM is only "memory safe" in that the memory accesses are contained to its sandbox (and part of the stack is outside of the heap and protected from stack overflows, like the return address). Everything else is still wide open for corruption and is easier to manipulate due to predictable function pointers.
The wasm shadow stack is a great idea for mitigating various attacks against C but since you can only put ints and floats on that stack and you can't pass values by reference, the vast majority of applications have a bunch of critical stack data stored in the heap at a fixed address next to non-critical data and a stray memcpy/strcpy can trample a function pointer. Function #37 will always be the same function and its arguments are probably being loaded from nearby locals on the stack.
If support for putting more complex types in arguments and locals ever ships in wasm, it would make it possible to mitigate a lot of this. I'm not sure whether that's in the cards, though.