Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
fix: remove destroyed toplevel resources from the tracking list (UAF)
handle_destroy_toplevel_resource freed the per-resource data but left
the resource pointer in CceWindowManagement.toplevels. Latent while
nothing iterated that list; send_grid_patch does, so restarting the
grid client crashed the session: the dead instance's entry lingered and
the new instance's first patch send dereferenced the freed resource.
Shadow-verified against the exact repro (kill + respawn cce-grid, both
instances patch and latch, compositor survives).
Co-Authored-By: Claude Fable 5 <[email protected]>
src/server/cce_window_management.rs | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/src/server/cce_window_management.rs b/src/server/cce_window_management.rs
index f81e1e7..fbab96f 100644
--- a/src/server/cce_window_management.rs
+++ b/src/server/cce_window_management.rs
@@ -143,6 +143,18 @@ pub unsafe extern "C" fn cce_wm_get_cce_toplevel(
unsafe extern "C" fn handle_destroy_toplevel_resource(resource: *mut ffi::wl_resource) {
let data_ptr = ffi::wl_resource_get_user_data(resource) as *mut CceToplevelData;
if !data_ptr.is_null() {
+ // Drop the tracking entry BEFORE the resource dies:
+ // send_grid_patch iterates `toplevels`, and a stale pointer there
+ // is a use-after-free on the next patch send — this crashed the
+ // session the first time the grid client was RESTARTED (the old
+ // entry lingered, the new instance's first patch dereferenced it).
+ let server = (*data_ptr).server;
+ if !server.is_null() {
+ (*server)
+ .cce_window_management
+ .toplevels
+ .retain(|&r| r != resource);
+ }
let _ = Box::from_raw(data_ptr);
}
}