Because the shadow stack is created and managed by the WebAssembly binary itself, it would be its responsibility to add protections like stack protectors or ASLR on it within the linear memory if it wants them. A WebAssembly JIT isn't ever going to touch the linear memory in a way the binary doesn't specify.
>But function addresses can still be randomly offset, no?
No, functions have fixed indexes in a WebAssembly binary. You can't dynamically reassign the indexes at runtime.
You do have the benefit that if your program tries to jump to a function index chosen by an attacker, the attacker can only jump to a function with a compatible type signature. The attacker can't do anything too clever like jumping partway into an arbitrary function, jumping into attacker-written code in the mutable linear memory, or queuing up a series of return addresses to pull off return-oriented-programming.
> C++ compiled to WebAssembly generally manages its own parallel "shadow stack" in linear memory
In the paper, we call the compiler-organized stack in linear memory the "unmanaged stack", to differentiate it from the "evaluation stack" (WebAssembly is a stack-based VM, so this contains arguments and results of instructions) and the "managed call stack" (contains call frames, return addresses, local variables. Managed by the VM, cannot be inspected explicitly by WebAssembly instructions).
> attacker can only jump to a function with a compatible type signature
This is true, but note that WebAssembly types are fairly low-level. That is, there are only four primitive types (i32/i64, f32/f64) and, e.g., a C function that takes a string (char *) and a size_t would be type-compatible with a function that takes a signed int and a struct pointer (all those four types map to the same Wasm type: i32).
Also the paper admits that security has been an important component of the design of wasm: "The design of WebAssembly includes various features to ensure binary security. For example, the memory maintained by a WebAssembly program is separated from its code, the execution stack, and the data structures of the underlying VM. To prevent type-related crashes and attacks, binaries are designed to be easily type-checked,which they are statically before execution. Moreoever, WebAssembly programs can only jump to designated code locations,a form of fault isolation that prevents many classic control flow attacks."