Finish interrupted copying collections to keep the GC heap consistent#13528
Conversation
Garbage collection is incremental with respect to the Wasmtime embedder and `Future::poll`, yielding between collection increments to cooperate with the async runtime; it is logically atomic with respect to the Wasm mutator. When the future driving an incremental collection is dropped before completion (e.g. an async `call_async` future is cancelled while a GC is in progress), the copying collector's heap was left half-flipped and half-forwarded. Subsequent store reuse observed the half-collected state and corrupted the GC heap. We fix this situation by forcing any in-progress collection to finish synchronously in `CopyingCollection`'s `Drop`. Other collectors do not actually do collection across multiple `Future::poll`s in any meaningful way and so cancellation does not lead to GC heap corruption for them. Fixes bytecodealliance#13516
|
Would it be possible to store the collection's intermediate state in the collector itself and automatically trigger a GC on future allocations? If a host is time-slicing a super long collection and ends up just having to block anyway to finish it that seems like something we'll ideally want to fix |
The problem is that even running Wasm code that doesn't allocate, but accesses GC object fields, will observe the half-collected state and crash (basically equivalent to running with GC heap corruption). We would need to have every API that could transitively access the GC heap automatically check for this state and trigger collection if needed, which is not just like I think the only realistic alternative to this PR would be to not make GC host-incremental at all, and if we ever want to have host-incremental GC then we would implement an actual concurrent-with-the-mutator incremental collector and do its incremental GC slices atomically and without interruption to avoid this same issue but "up a level" |
|
Good points yeah. I've filed #13530 to track this over time, but I agree there's really not a whole lot else we can do in the meantime. |
Garbage collection is incremental with respect to the Wasmtime embedder and
Future::poll, yielding between collection increments to cooperate with the async runtime; it is logically atomic with respect to the Wasm mutator.When the future driving an incremental collection is dropped before completion (e.g. an async
call_asyncfuture is cancelled while a GC is in progress), the copying collector's heap was left half-flipped and half-forwarded. Subsequent store reuse observed the half-collected state and corrupted the GC heap.We fix this situation by forcing any in-progress collection to finish synchronously in
CopyingCollection'sDrop. Other collectors do not actually do collection across multipleFuture::polls in any meaningful way and so cancellation does not lead to GC heap corruption for them.Fixes #13516