Closed aykevl closed 2 years ago
Apparently what happens is this:
internal/task.start
allocates some memory(*internal/task.state).initialize
allocates some more memoryruntime.markStack
only marks the objects in (*internal/task.state).initialize
, not in internal/task.start
.@niaow any idea what might be going on? I'm suspecting a problem with wasm-opt, probably Asyncify. With a quick comparison, I found that (*internal/task.state).initialize
is a lot bigger after wasm-opt, so it looks like it had the Asyncify pass applied to it.
From my initial look at the IR, the internal/task.start
function seems to be removing its element of the stack chain early (before calling initialize
), which creates a gap when the task is not tracked.
I think it is a bug with this part of the stack slots pass, which places the stack chain restore after the last time a pointer is created locally. However, when an allocation is done in a function after the last time a local pointer is created, this can cause a use-after-free: https://github.com/tinygo-org/tinygo/blob/cf0b7edc78e42038a0bb522b3f1a5b76928e730e/transform/gc.go#L275-L303
In order to fix this, I think we need to strip the tail flags and put this immediately before the return.
Another interesting bug is that the MakeGCStackSlots
pass does not treat the runtime.GC
function as a garbage collection trigger.
Currently working on some fixes, although it looks like we may have some other bugs too. . .
This fix was released in TinyGo v0.24.0
so now closing.
While trying to enable ThinLTO on WebAssembly, I've found an issue with memory corruption. I managed to reproduce it with a small patch that should be harmless but triggers this bug.
What it does, is:
And then I run any program like this:
(The
-opt=1
is important, otherwise it won't reproduce).Apparently
t.state.entry
is zero, which it shouldn't be.