-
Notifications
You must be signed in to change notification settings - Fork 75
Use wasm start-function linking and __cxa_atexit #25
Description
.init_array/.fini_array and .ctors/.dtors have a history of being exploitable. For example, "Abusing .CTORS and .DTORS For FUN and PROFIT", and other examples are easy to find. Modern ELF systems have mitigated it with RELRO which makes these sections read-only. However, WebAssembly doesn't support read-only memory. This may change in the future (or perhaps we could use a separate linear memory space), though at present there are no proposals. If we implement traditional .init_array/.fini_array support now, we'd be opening up an attack vector.
WebAssembly would be less vulnerable than traditional ELF sysystems without RELRO, because wasm's indirect calls can only call into defined function entry points, and type signatures have to match, however theoretical exploits are still possible.
I propose to avoid .init_array and .fini_array, and instead:
- use wasm start functions for initialization code, and add support for linking them, rather than use
.init_array, as suggested here. - lower destructor code, such as
__attribute__((destructor)), into functions registered with__cxa_atextby initializers. I've implemented this in LLVM here.
To be sure, with current __cxa_atexit implementations, function pointers are still stored in writeable linear memory, so the problem already exists. However, .init_array/.fini_array are dense arrays of pointers, making them easier to hit, and they're more likely to live at a predictable address.
__cxa_atexit is also generally more robust than .fini_array because
- If cleanups are registered dynamically while global ctors are being run, it properly orders all the cleanups, including the dynamically registered ones, in the reverse order.
- If the process exits before all the global constructors have run, it doesn't run destructors that haven't been registered yet.
This approach also doesn't preclude implementing .init_array and .fini_array in the future. We could always add support for .fini_array in the tools without breaking the ABI.
The main downside of this approach is that it's different from how other platforms work, and would require more adaptation when porting libc and other low-level tools. However, I believe it's worth the effort.