From 93e5ed7e6cc4a0236aead90567d1c62471a3f59e Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 21:08:52 +0000 Subject: [PATCH] Fix use-after-move memory corruption in Wayland backends - In `winit.rs`, heap-allocate `Wm` with `Box::new` so its address does not change when moved into the `event_loop.run` closure. This prevents `WaylandState.globals` from being a dangling pointer. - In `drm.rs`, allocate `Wm` in `Rc>` before obtaining a reference to `wm.g` to avoid the same dangling pointer issue. - Fix `drm.rs` `run_event_loop` borrowing issue by accepting `Rc>` instead of taking a long-lived `RefMut`, allowing event sources like libinput to access it without a `BorrowMutError`. - Update `WaylandBackend::state` to hold an `Option>` so that calling `with_state` before initialization doesn't segfault. Co-authored-by: paperbenni <15818888+paperbenni@users.noreply.github.com> --- src/backend/wayland/mod.rs | 10 ++++----- src/wayland/runtime/drm.rs | 42 +++++++++++++++++++++++------------- src/wayland/runtime/winit.rs | 2 +- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/backend/wayland/mod.rs b/src/backend/wayland/mod.rs index ff637bc9..72c2ae35 100644 --- a/src/backend/wayland/mod.rs +++ b/src/backend/wayland/mod.rs @@ -62,18 +62,18 @@ use std::ptr::NonNull; use crate::backend::wayland::compositor::WaylandState; pub struct WaylandBackend { - state: RefCell>, + state: RefCell>>, } impl WaylandBackend { pub fn new() -> Self { Self { - state: RefCell::new(NonNull::dangling()), + state: RefCell::new(None), } } pub fn attach_state(&self, state: &mut WaylandState) { - *self.state.borrow_mut() = NonNull::from(state); + *self.state.borrow_mut() = Some(NonNull::from(state)); } pub fn close_window(&self, window: WindowId) -> bool { @@ -120,8 +120,8 @@ impl WaylandBackend { } pub(crate) fn with_state(&self, f: impl FnOnce(&mut WaylandState) -> T) -> Option { - let mut ptr = *self.state.borrow(); - Some(unsafe { f(ptr.as_mut()) }) + let ptr = *self.state.borrow(); + ptr.map(|mut p| unsafe { f(p.as_mut()) }) } } diff --git a/src/wayland/runtime/drm.rs b/src/wayland/runtime/drm.rs index ea2a5b15..55ef9dc7 100644 --- a/src/wayland/runtime/drm.rs +++ b/src/wayland/runtime/drm.rs @@ -42,9 +42,13 @@ pub fn run() -> ! { log::info!("Starting DRM/KMS backend"); ensure_dbus_session(); - let mut wm = Wm::new(WmBackend::new_wayland(WaylandBackend::new())); - if let Some(wayland) = wm.backend.wayland_data_mut() { - init_wayland_globals(&mut wm.g, wayland); + let wm_cell = Rc::new(RefCell::new(Wm::new(WmBackend::new_wayland(WaylandBackend::new())))); + { + let mut wm = wm_cell.borrow_mut(); + let wm_ref = &mut *wm; + if let Some(wayland) = wm_ref.backend.wayland_data_mut() { + init_wayland_globals(&mut wm_ref.g, wayland); + } } let event_loop: EventLoop = EventLoop::try_new().expect("event loop"); @@ -56,12 +60,19 @@ pub fn run() -> ! { let display: Display = Display::new().expect("wayland display"); let mut state = WaylandState::new(display, &loop_handle); - state.attach_globals(&mut wm.g); - if let WmBackend::Wayland(data) = &mut wm.backend { + + // Unsafe: Obtain a pointer to the inner Globals and extend its lifetime. + // This is safe because the `Wm` instance is allocated on the heap inside `Rc>` + // and will not move. + let globals_ptr = &mut wm_cell.borrow_mut().g as *mut _; + state.attach_globals(unsafe { &mut *globals_ptr }); + + if let WmBackend::Wayland(data) = &mut wm_cell.borrow_mut().backend { data.backend.attach_state(&mut state); } { + let mut wm = wm_cell.borrow_mut(); let mut ctx = wm.ctx(); crate::keyboard_layout::init_keyboard_layout(&mut ctx); } @@ -94,10 +105,10 @@ pub fn run() -> ! { let (total_width, total_height) = compute_total_dimensions(&output_surfaces); - crate::wayland::render::drm::sync_monitors_from_outputs_vec(&mut wm.g, &output_surfaces); + crate::wayland::render::drm::sync_monitors_from_outputs_vec(&mut wm_cell.borrow_mut().g, &output_surfaces); { use crate::monitor::update_geom; - update_geom(&mut wm.ctx()); + update_geom(&mut wm_cell.borrow_mut().ctx()); } let shared = init_shared_state(&output_surfaces, total_width, total_height); @@ -106,11 +117,10 @@ pub fn run() -> ! { spawn_xwayland(&state, &loop_handle); // Initialize Wayland systray runtime - only applicable for Wayland backend - if let WmBackend::Wayland(data) = &mut wm.backend { + if let WmBackend::Wayland(data) = &mut wm_cell.borrow_mut().backend { data.wayland_systray_runtime = crate::systray::wayland::WaylandSystrayRuntime::start(); } - let wm_cell = Rc::new(RefCell::new(wm)); let wm_cell_for_closure = Rc::clone(&wm_cell); let mut libinput_context = @@ -173,7 +183,7 @@ pub fn run() -> ! { run_event_loop( event_loop, - &mut wm_cell.borrow_mut(), + Rc::clone(&wm_cell), &mut state, &shared, &mut output_surfaces, @@ -316,7 +326,7 @@ fn setup_drm_vblank_handler( #[allow(clippy::too_many_arguments)] fn run_event_loop( mut event_loop: EventLoop, - wm: &mut Wm, + wm_cell: Rc>, state: &mut WaylandState, shared: &Arc>, output_surfaces: &mut [OutputSurfaceEntry], @@ -332,11 +342,13 @@ fn run_event_loop( event_loop .run(Duration::from_millis(16), state, move |state| { + let mut wm = wm_cell.borrow_mut(); + process_completed_crtcs(state, shared, output_surfaces); - super::common::arrange_layout_if_dirty(wm, state); + super::common::arrange_layout_if_dirty(&mut wm, state); - process_ipc(ipc_server, wm, shared); + process_ipc(ipc_server, &mut wm, shared); if wm.g.dirty.input_config { wm.g.dirty.input_config = false; @@ -356,12 +368,12 @@ fn run_event_loop( } } - process_animations(wm, state, shared); + process_animations(&mut wm, state, shared); process_cursor_warp(state, &pointer_handle, shared); render_outputs( - wm, + &mut wm, state, renderer, output_surfaces, diff --git a/src/wayland/runtime/winit.rs b/src/wayland/runtime/winit.rs index 7d32a37d..67538729 100644 --- a/src/wayland/runtime/winit.rs +++ b/src/wayland/runtime/winit.rs @@ -32,7 +32,7 @@ use crate::wm::Wm; /// Run the winit (nested) Wayland compositor. pub fn run() -> ! { ensure_dbus_session(); - let mut wm = Wm::new(WmBackend::new_wayland(WaylandBackend::new())); + let mut wm = Box::new(Wm::new(WmBackend::new_wayland(WaylandBackend::new()))); if let Some(wayland) = wm.backend.wayland_data_mut() { init_wayland_globals(&mut wm.g, wayland); }