-
Notifications
You must be signed in to change notification settings - Fork 181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] Stack overflow silently corrupts data and vice versa #255
Comments
I missed this whole conversation, but we pass in the stack size at compile time, do we not? (we do for other languages)... can't that just be compared against the stack pointer to find out if it's overflows? (not as nice as a 0 comparison for sure, but still)... |
Try to convince the compiler to insert these checks. Otherwise WASM-4 uses only one page for the whole RAM, so no fancy paging tricks there (if they even exist). EDIT: also there's no reliable way i think to get stack pointer value from the runtime. |
Couldn't you also do this with a sentinel tag at the end of the stack area? And just detect if it's overwritten each frame (it should never be touched)? You'd have to add this code manually, but if Rust isn't going to help us... |
What if it is overwritten by the same value? Also by the end of the |
The only way to fix this bug in backwards is to add ability to move memory map somewhere else. We could move these memory maps to the end of the RAM, but it would break ABI, which may or may not be ok. |
Proposed solutionsExport
|
VERY, VERY unlikely, you'd use a sequence of bytes... 8 or 16 should be safe enough and make this as unlikely a monkey typing shakespeare... and this could be done easily without ANY special runtime support from Rust at all. Just a tiny call added to the end of
Doesn't matter, the sentinel value would have already been destroyed and that's what we're detecting, not the size of the stack at the moment, but rather the RESULTS of it having overflowed just a bit ago... I'm not inventing this technique, it's a thing that's real and been used for exactly this type of thing in the past - sorry if I don't have a name for the concept off the top of my head. |
Please explain exactly what you are moving when you say "memory map"... do you mean the entire documented registers and VRAM, etc? That sounds like a no go just to make one language happy that's treating their start differently than everyone else. |
The whole thing: https://wasm4.org/docs/reference/memory |
IMHO: This is simply something our "hardware" doesn't allow because of our hard-wired RAM layout. I personally don't think we should change WASM-4 overall (or add complexity) to accommodate a single compiler option from a single language. (even IF it is an agreeably useful feature) If it was to be considered at all it should be only after having exhausted all simpler options (that are already possible, and do not requires any changes to WASM-4). As I pointed out there are already simple ways to do this easily enough (just like you'd do it in any of the other languages we support)... just nothing you get "for free"... of course it's not truly "for free" at all if it requires a lot of changes to the WASM-4 "hardware" just to support it... |
@joshgoebel your fix is still bad. It's not always reliable and detects stack overflows only at the end of the |
@joshgoebel I have added a warning in the top comment for you. |
How?
True, but the fix is currently only relevant for Rust as I understand it - correct me if I'm mistaken. ?
It was put forth as a suggestion that does not require changing the entire WASM-4 architecture. So you have to judge in inside of that context, not in an absolute sense as I think you are. :) |
Ok, so it seems the idea is that stack pointer would underflow (well it's likely a u32, so it'd overflow) and roll around to So, my next question: which languages can even move the stack to low RAM (at compile time) other than Rust? |
Something could override a return pointer.
This was only my estimate of your solution. |
If you can pass arguments to the |
The actual function call stack is in the WASM VM, not the memory... no? Though I suppose technically with indirect calls this might be possible if a function pointer was corrupted on the stack? So perhaps that's what you're referring to or I'm missing some nuance of WASM I don't grok yet.
This assumes someone has a current LLVM installed...? I'm using Zig (from website) and C/C++ (from Xcode tools) and I don't have |
I invite you to read the WebAssembly specification. Small hint: wasm compiles down to assembly with runtime checks.
I am not suggesting to install |
I'm not entirely unfamiliar... but the reason WASM has opcodes like From MDN:
Those parameters are passed on the stack on the WASM side (outside stack), not inside the VM memory (from what I'm reading). If they were using the stack in RAM (inside stack) then you'd see code working with global 0 (the stack pointer in all the disassemblies I've compiled), something like:
Except you'd have a In my experience so far with Zig and WASM the inside stack is mostly being used for local memory allocation of large variables inside functions (like strings, large arrays, etc)... things that don't map super well to locals. |
@joshgoebel let's not focus on it. It's not relevant. The bug is pretty bad anyway as it triggers UB. |
I'm not too crazy about any of the proposed solutions. I think having WASM-4's system memory at a variable address would introduce too much complexity. The proposal about moving the WASM-4 system memory after the stack but before the global base is interesting but not really feasible at least for LLVM-based languages (the At the moment this is affecting primarily Rust because the linker there uses
|
Yeah, these steps are do not break anything, so they're ok to implement. I wouldn't say relocatable memory map adds any complexity, compared to undefined behaviour on a stack overflow, but we could go for the hard solution. And one last thing...
It's not. It just moves data segment, not the stack. |
WARNING: this bug is relevant for every language!
To detect stack overflow
rustc
places stack before anything else (see explanation).As you may know, stack grows backwards. Therefore to detect stack overflow it is basically placed at the top of the module's memory, and if stack pointer underflows and then is dereferenced, it throws an exception. Because of the memory map being at the top, it is not possible to detect stack overflow and it allows for stack overflow to silently corrupt some data on its way or vice versa.
See solutions: #255 (comment)
I want to fix this bug, but we all need to agree on a solution.
The text was updated successfully, but these errors were encountered: