Commit 9b0393e
committed
fix(borders): prevent use-after-free in border destruction
This commit fixes a use-after-free bug in the border_manager that was
causing crashes with exception code 0xc000041d when borders were being
destroyed during workspace/monitor changes.
The root cause was a race condition between the main thread destroying a
border window and the border's window thread still processing queued
window messages (WM_PAINT, EVENT_OBJECT_LOCATIONCHANGE). The crash
occurred when these callbacks invoked render_target.EndDraw(), which
internally calls HwndPresenter::Present() to present the rendered frame
to the HWND. By this point, the HWND and its associated DirectX surfaces
had already been freed, resulting in Direct2D attempting to dereference
freed memory (0xFEEEFEEEFEEEFEEE - Windows heap poison value).
The issue stemmed from two problems in destroy_border():
1. Direct2D resources (render_target and brushes) were not being
released before closing the window. These COM objects hold internal
pointers to the HWND and its DirectX swap chain/surfaces. When
close_window() was called, Windows began tearing down these resources
while the Direct2D objects still held dangling pointers to them.
2. GWLP_USERDATA was not being cleared before closing the window. This
meant that any messages already queued in the window's message queue
could still retrieve the border_pointer and attempt to render using
the now-invalid Direct2D resources.
This commit addresses both issues:
- In destroy_border() (mod.rs): Explicitly set render_target to None and
clear the brushes HashMap before calling destroy(). This ensures that
Direct2D COM objects are properly released while the HWND is still
valid, preventing EndDraw() from accessing freed HWND resources.
- In destroy() (border.rs): Clear GWLP_USERDATA before calling
close_window(). This ensures that any pending window messages will see
a null pointer and exit early from the callbacks (which already have
null pointer checks in place).
These changes create two layers of defense against the race condition:
the callbacks won't access the border_pointer (it's null), and even if
they somehow did, the render_target would be None so no rendering
operations would occur.
I think this is the root cause of a lot of crash tickets that people are
mistakenly attributing to specific applications which lack
reproducibility across different users/machines, i.e. #1626, #1624.1 parent 10af1f3 commit 9b0393e
2 files changed
+12
-0
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
313 | 313 | | |
314 | 314 | | |
315 | 315 | | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
316 | 321 | | |
317 | 322 | | |
318 | 323 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
767 | 767 | | |
768 | 768 | | |
769 | 769 | | |
| 770 | + | |
| 771 | + | |
| 772 | + | |
| 773 | + | |
| 774 | + | |
| 775 | + | |
| 776 | + | |
770 | 777 | | |
771 | 778 | | |
772 | 779 | | |
| |||
0 commit comments