This was an issue caught at Weka, as part of our added support for AArch64. The issue require specific conditions to trigger, briefly code that uses the affected registers and yields + gc collect in between switching back.
On ARM we have flags to enable aarch64 neoverse CPU extension flags that enables codegen with loop vectorization passes. As a consequence of supporting neoverse, any compiler is free to use v8 to v15 registers that are 128-bit that overlaps the d8 to d15 64-bit registers. Even for normal operations those specific floating-point registers could be used to store pointers, nothing prevents us from doing so, although, its more likely that those v8 to v15 registers vectorize code that split them into two a lower and higher part.
The ABI spec tells the lower 64-bit part of those v8 to v15, that coincides with d8 to d15, need to be saved, although druntime fibers switch code seem to intentionally and wrongly skip it from GC scans because they assume a user won't do that, where they are only used for floating-points, which is a blind assumption. Again, nothing prevents the user to inline assembly such code or actually, the compiler to lower code to use these 64-bit registers or the lower part (on vectorization code) to store actual pointers.
Because of all this, GC scans skip pointers stored in the affected registers, and, on collection, invalidate those pointers that are only referenced in them. If in between a call that uses these registers, a switch out of the context occurs plus a collection before switch back in, then, after switching back, the pointers that got saved by the switch code inside registers but not pointed by fibers stack gets wrongly collected by GC, since it doesn’t know of such pointer being live.
This was an issue caught at Weka, as part of our added support for AArch64. The issue require specific conditions to trigger, briefly code that uses the affected registers and yields + gc collect in between switching back.
On ARM we have flags to enable aarch64 neoverse CPU extension flags that enables codegen with loop vectorization passes. As a consequence of supporting neoverse, any compiler is free to use
v8tov15registers that are 128-bit that overlaps thed8tod1564-bit registers. Even for normal operations those specific floating-point registers could be used to store pointers, nothing prevents us from doing so, although, its more likely that thosev8tov15registers vectorize code that split them into two a lower and higher part.The ABI spec tells the lower 64-bit part of those
v8tov15, that coincides withd8tod15, need to be saved, although druntime fibers switch code seem to intentionally and wrongly skip it from GC scans because they assume a user won't do that, where they are only used for floating-points, which is a blind assumption. Again, nothing prevents the user to inline assembly such code or actually, the compiler to lower code to use these 64-bit registers or the lower part (on vectorization code) to store actual pointers.Because of all this, GC scans skip pointers stored in the affected registers, and, on collection, invalidate those pointers that are only referenced in them. If in between a call that uses these registers, a switch out of the context occurs plus a collection before switch back in, then, after switching back, the pointers that got saved by the switch code inside registers but not pointed by fibers stack gets wrongly collected by GC, since it doesn’t know of such pointer being live.