-
Notifications
You must be signed in to change notification settings - Fork 35
Fix use-after-move memory corruption in Wayland backends #238
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<WaylandState> = EventLoop::try_new().expect("event loop"); | ||
|
|
@@ -56,12 +60,19 @@ pub fn run() -> ! { | |
|
|
||
| let display: Display<WaylandState> = 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<RefCell<Wm>>` | ||
| // and will not move. | ||
| let globals_ptr = &mut wm_cell.borrow_mut().g as *mut _; | ||
| state.attach_globals(unsafe { &mut *globals_ptr }); | ||
|
Comment on lines
+64
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: No, it is not sound. Taking &mut field from RefCell::borrow_mut, casting it to *mut T, and storing it as a long-lived &'static mut T (via &mut *ptr as &'static mut T or similar) bypasses RefCell's runtime borrow tracking. The RefMut guard drops and clears the mutable borrow flag, allowing subsequent borrow_mut calls on the RefCell to succeed at runtime without panicking. However, dereferencing the long-lived &'static mut T creates a mutable reference while another mutable borrow (from the new borrow_mut) is active, violating Rust's aliasing rules and causing undefined behavior. Citations:
🏁 Script executed: # First, let's find the WaylandState struct and its attach_globals method
fd -t f "\.rs$" src/wayland | head -20Repository: instantOS/instantWM Length of output: 634 🏁 Script executed: # Search for attach_globals method definition
rg -A 10 "fn attach_globals" --type rustRepository: instantOS/instantWM Length of output: 1127 🏁 Script executed: # Find the run_event_loop function
rg -A 15 "fn run_event_loop" --type rustRepository: instantOS/instantWM Length of output: 1111 🏁 Script executed: # Check winit.rs for similar patterns
fd -t f "winit\.rs$"Repository: instantOS/instantWM Length of output: 145 🏁 Script executed: # Look at the full drm.rs file context to understand the issue
wc -l src/wayland/runtime/drm.rsRepository: instantOS/instantWM Length of output: 93 🏁 Script executed: # Get the full WaylandState struct definition
rg -B 5 -A 30 "struct WaylandState" src/backend/wayland/compositor/state.rsRepository: instantOS/instantWM Length of output: 1505 🏁 Script executed: # Check the globals field type and how it's accessed
rg -A 5 "fn globals_mut" src/backend/wayland/compositor/state.rsRepository: instantOS/instantWM Length of output: 275 🏁 Script executed: # Look at the context around run_event_loop to see borrow patterns
rg -A 50 "fn run_event_loop" src/wayland/runtime/drm.rs | head -80Repository: instantOS/instantWM Length of output: 1928 🏁 Script executed: # Check winit.rs runtime for similar patterns
rg -B 5 -A 15 "attach_globals\|run_event_loop" src/wayland/runtime/winit.rsRepository: instantOS/instantWM Length of output: 45 🏁 Script executed: # Find the globals field definition in WaylandState
rg "globals:" src/backend/wayland/compositor/state.rsRepository: instantOS/instantWM Length of output: 223 🏁 Script executed: # Look for all usages of globals_mut in the codebase
rg "globals_mut()" src/wayland/ -A 3Repository: instantOS/instantWM Length of output: 2181 🏁 Script executed: # Check the full WaylandState struct to find globals field
sed -n '/^pub struct WaylandState/,/^}/p' src/backend/wayland/compositor/state.rs | grep -A 2 -B 2 "globals"Repository: instantOS/instantWM Length of output: 302 🏁 Script executed: # Look at winit.rs runtime initialization to see if it has the same pattern
cat -n src/wayland/runtime/winit.rs | head -100Repository: instantOS/instantWM Length of output: 4469 🏁 Script executed: # Check if arrange_layout_if_dirty or other event loop functions call globals_mut
rg "globals_mut()" src/wayland/runtime/common.rs -B 5 -A 5Repository: instantOS/instantWM Length of output: 447 🏁 Script executed: # Look at the event loop closure more carefully - see all calls
sed -n '28,100p' src/wayland/runtime/drm.rsRepository: instantOS/instantWM Length of output: 2634 🏁 Script executed: # Check if ctx operations during event loop could call globals_mut
rg "arrange_layout_if_dirty|process_ipc|process_animations" src/wayland/runtime/drm.rs -A 3Repository: instantOS/instantWM Length of output: 1011 🏁 Script executed: # Verify that ctx.core_mut() or similar gives access to globals_mut
rg "fn core_mut|fn globals_mut" src/ -B 2 -A 5Repository: instantOS/instantWM Length of output: 1248 🏁 Script executed: # Check if winit.rs really has the same issue - look for Rc<RefCell> or attach_globals patterns
rg "Rc<RefCell<Wm>>|attach_globals" src/wayland/runtime/winit.rsRepository: instantOS/instantWM Length of output: 99 🏁 Script executed: # Also check winit init path more carefully
cat -n src/wayland/runtime/winit.rs | sed -n '30,50p'Repository: instantOS/instantWM Length of output: 1006 🏁 Script executed: # Verify the event loop in winit and see if it keeps wm alive
rg "event_loop.run|run_app" src/wayland/runtime/winit.rs -A 20Repository: instantOS/instantWM Length of output: 45 🏁 Script executed: # Check the full winit event loop to see if wm is kept alive during event processing
sed -n '100,200p' src/wayland/runtime/winit.rsRepository: instantOS/instantWM Length of output: 3305 🏁 Script executed: # Look for the event_loop.run closure in winit
rg -A 30 "event_loop.run\|run_app" src/wayland/runtime/winit.rsRepository: instantOS/instantWM Length of output: 45
The fix requires changing how 🤖 Prompt for AI Agents |
||
|
|
||
| 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<WaylandState>, | ||
| wm: &mut Wm, | ||
| wm_cell: Rc<RefCell<Wm>>, | ||
| state: &mut WaylandState, | ||
| shared: &Arc<Mutex<SharedDrmState>>, | ||
| 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, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
issue (bug_risk): The raw pointer/lifetime extension for
wm.gcan violate aliasing andRefCellborrowing guarantees.This pattern takes a
&mut Globalsout ofRefCell<Wm>’s borrow tracking and Rust’s aliasing model. Ifattach_globalsstores that&mut Globalsfor later use (e.g., across event-loop iterations), laterwm_cell.borrow_mut()calls can overlap mutable access towm.g(and other fields) while the stored&mutis still live, which is UB even ifRefCelldoesn’t panic.Prefer instead:
WaylandStatehold anRc<RefCell<Globals>>/Rc<Wm>and borrow when needed, orattach_globalsto take a callback/handle instead of a long-lived&mut Globals.If
attach_globalsuses the reference only within the call and does not store it, you don’t need the raw pointer orunsafe; just callstate.attach_globals(&mut wm_cell.borrow_mut().g);in a small scope so the borrow ends promptly.