mirror of
https://github.com/zed-industries/zed.git
synced 2026-07-31 10:38:41 +00:00
GPUI allocates every element for a draw in a single per-App bump arena, and `Arena::clear()` assumed no draw was in progress. But draws can nest: on Windows, the window procedure re-enters whenever the main thread pumps messages mid-draw (cross-thread `SendMessage` dispatch, modal message loops entered by COM/OLE calls such as clipboard reads), and on any platform a draw can be triggered from within another draw (e.g. `open_window`). When a nested draw finished, its arena clear freed and rewound memory the outer draw was still using. The `ArenaBox` validity flag only guards new derefs, so `&mut` references already held by the outer draw silently pointed into reused memory. That use-after-free corrupted element-state keys (`GlobalElementId`/`SharedString` Arcs) and heap metadata, crashing later in innocent-looking frames — seen in the wild as `EXCEPTION_ACCESS_VIOLATION_READ / 0xffffffffffffffff` in `Frame::finish` and element allocation ([ZED-9QN](https://zed-dev.sentry.io/issues/7577818040/), [ZED-7JC](https://sentry.io/organizations/zed-dev/issues/7465778926/), [ZED-7C6](https://zed-dev.sentry.io/issues/7460258444/)), plus occasional "attempted to dereference an ArenaRef after its Arena was cleared" panics (ZED-96P, ZED-8XN), Windows-dominant and spanning versions 1.1.7–1.8.2. The fix makes the arena sound under nesting: `ElementArenaScope` tracks a scope depth on the arena, and `Arena::clear()` is deferred while any scope is active — the outermost draw's clear drops both draws' allocations. Since arena chunks are stable heap blocks and allocation only appends, nested allocation was already safe; the mid-draw clear was the only destructive operation. Scopes are ended via a consuming `exit(arena)` call that asserts arena identity (by pointer comparison, never dereferencing), and `ArenaClearNeeded::clear(cx)` reaches the arena through the App, so this bookkeeping contains no unsafe code beyond what existed before. A panic that unwinds a draw balances the scope depth in the guard's `Drop`, so later clears still run; only the unwound draw's own clear token is never produced, leaving the arena populated until the next draw's clear — a one-frame leak at worst, never a use-after-free. Additionally, draws are no longer run re-entrantly: GPUI's `on_request_frame` callback skips requests that arrive while a draw is already on the thread's stack (remembering `force_render` for the next frame), and on Windows a `DrawCoordinator` owned by the platform and shared with every window additionally guards the wider `draw_window` span (presentation, IME updates). Deferred windows validate their update region (so nested message pumps don't busy-loop on WM_PAINT) and are repainted at most one vsync later by the vsync thread's existing per-tick invalidation. An integration test opens a window from within an element's paint; without the deferred clear it reproduces the exact "ArenaRef after its Arena was cleared" panic seen in the wild. Deferrals are logged so the diagnosis can be confirmed from user logs. Closes FR-110 Closes FR-114 Release Notes: - Fixed a crash on Windows caused by re-entrant window drawing corrupting UI element memory. |
||
|---|---|---|
| .. | ||
| src | ||
| Cargo.toml | ||
| LICENSE-GPL | ||