Code change:
Crash keys to help narrow down the source of crash
is the first build with above crash keys annotated, result Wrapper class is not the one from the list, need to add the missing ones , , , and
Crash has not been triggered with updating to Electron 27, closing optimistically.
I am not sure how to verify this but I will monitor again for new integration test crashes if there are any crashes attached.
We have a new instance of this crash with Electron 28, reopening for debugging.
Had a new failure with Electron 29 as well, refs Sadly I missed porting the gin annotations to 29 branch
Another run
Another crash
Seems like the crash rate of this particular one has increased again, adding back the crash keys again.
Added crash keys and triggered test builds to debug further. Work can be tracked in
I have now tracked down the source of crash, but before I get down into the final details lets walk through the nature of the crash and understand what is happening. The crash started appearing a year ago in our CI mainly with integration tests across all 3 platforms inconsistently. Sometimes the crash would disappear after runtime updates but recently it has been pretty consistent in our Windows CI. Although I was unsuccessful to repro it locally but given its frequent presence in CI I decided to rely on which are meant for cases like this. On shutdown of the main process, the runtime eventually has to gracefully cleanup the environment and its associated tasks from the event loop which in rare cases lead to UAF in certain listeners. The initial reports point to the resource cleanup in Newer reports point to the resource cleanup in The runtime exposes JS api that are backed by C++ implementations through V8. is the base class that allows exposing C++ objects as corresponding V8 objects. For example to expose a following object in Javascript, This is how we would do the same in C++ and expose it through V8 The work to construct an object instance from this C++ class will eventually be performed by . works in a similar fashion but it is limited to holding the C++ function pointers that needs to be invoked from V8. In the above example, these pointers would be and . Now going back to the implementation of , you will see that a parameter called is passed in to the function. This is the pointer to the static member from the above example which can store metadata about the object, in this case it about the type of owner of the object in C++. The primary use case of this pointer in is to act as a cache key to store the object templates. The second point of interest in this implementation is the call to which acts the primary resource management hook between the C++ heap and Javascript heap. This works pretty much the same as how . We request a callback to be invoked when the corresponding object is garbage collected. You can refer to for why there are two separate phases of callback registration in the C++ version. class 1) It could be deleted when the V8 isolate gets disposed ;l=39-45 2) Or, it could be deleted when the second weak callback fires The order of destruction between which is held by and guarantees that the destruction entry points don't race against each other. I will leave the analysis of this destruction sequence to the interested readers. The initial reports which don't appear now could be attributed to consuming the fix from https://chromium- and I can only speculate about this as there is not much we can understand from the old reports. class 1) It could be destroyed when the second weak callback fires ;l=39 But there is another scenario of destruction happening during shutdown that is not captured. This is where crash keys come in handy. The keys I are in 1) Extended the to hold the name of the C++ class that is impacted 2) Extended the with a new boolean that will be set to when is called. This will help in cases where either of the weak callbacks are called after the class is destroyed. 3) I also want to see the other way, so new boolean member to which will be set to when the second weak callback is called. We already have the member that tracks whether the first weak callback was called. Now in we can have condition that basically checks which can confidently pinpoint that someelse destroyed this class between the first and second callbacks. After a couple of retries in the CI, I was able to trigger the crash As expected I had a bunch of dumps from and one for the actual crash. This is how they looked, see the size of the crash dumps to know which one had the crash keys, <img width="772" alt="Screenshot 2024-09-26 at 12 54 17" src=""Crash key values Run DumpWithoutCrashing stack Actual crash stack So we had a class in Electron that worked in theory similar to which destroys the C++ object on the disposal of isolates in concern that the finalization callbacks might have not run But the only issue is that function runs at the destruction of the microtasks runner which can race with the pending task flushing from the event loop on destruction of as seen from the stack traces. 1) Convert into a that actually watches destruction of 2) Clear weakness for the wrapper inside so that we can do the following in second weak callback If you have made it this far, hope you enjoyed this debugging session! Now this disjoint between the C++ heap and Javascript heap leading to these uaf issues is not new. The V8 team had already worked on addressing it for the Blink rendering engine via the project called and it is also available as a standalone library for embedding in any C++ project that uses V8 Through this library, the trace based garbage collector can now view both the C++ heap and Javascript heap together to help make lifetime management simpler. IIUC, could adopt cppgc when the heap setup mechanisms are moved from blink into V8 as part of We can then explore the adoption in Electron.

Related discussion:
text: disturl="https://electronjs.org/headers" target="30.5.1" <del> ms_build_id="10262041" </del> <ins> ms_build_id="10306386" </ins> runtime="electron" build_from_source="true" legacy-peer-deps="true"