From d73f621455a2b3fc16149977d4a39d81dc35ce8d Mon Sep 17 00:00:00 2001 From: Val Packett Date: Sat, 21 Feb 2026 00:09:37 -0300 Subject: [PATCH 1/2] rutabaga: fix access mask WRITE being converted to READ In upstream rutabaga, this is moved into the mesa3d utils crate which does have it fixed already. Signed-off-by: Val Packett --- src/rutabaga_gfx/src/rutabaga_os/sys/unix/memory_mapping.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rutabaga_gfx/src/rutabaga_os/sys/unix/memory_mapping.rs b/src/rutabaga_gfx/src/rutabaga_os/sys/unix/memory_mapping.rs index d948efb4e..581d3c971 100644 --- a/src/rutabaga_gfx/src/rutabaga_os/sys/unix/memory_mapping.rs +++ b/src/rutabaga_gfx/src/rutabaga_os/sys/unix/memory_mapping.rs @@ -48,7 +48,7 @@ impl MemoryMapping { let non_zero_opt = NonZeroUsize::new(size); let prot = match map_info & RUTABAGA_MAP_ACCESS_MASK { RUTABAGA_MAP_ACCESS_READ => ProtFlags::PROT_READ, - RUTABAGA_MAP_ACCESS_WRITE => ProtFlags::PROT_READ, + RUTABAGA_MAP_ACCESS_WRITE => ProtFlags::PROT_WRITE, RUTABAGA_MAP_ACCESS_RW => ProtFlags::PROT_READ | ProtFlags::PROT_WRITE, _ => return Err(RutabagaError::SpecViolation("incorrect access flags")), }; From 2d739bd94c7a95c40ea1633f24fc820b35f81cf2 Mon Sep 17 00:00:00 2001 From: Val Packett Date: Thu, 19 Feb 2026 20:59:46 -0300 Subject: [PATCH 2/2] rutabaga: do not rely on seals to detect read-only shm fds Some compositors send read-only but not sealed memfds (at least wlroots does so for dmabuf feedback), so seal-based permission detection would result in mapping RO memory as RW and the guest application crashing. Fix by properly checking the access mode. Keep seal detection because e.g. Smithay based compositors do send O_RDWR but write-sealed memfds. Upstream rutabaga does not do any detection yet, but defaults to RO for all shm/memfd (!!) and RW for dmabuf. Signed-off-by: Val Packett --- src/rutabaga_gfx/src/cross_domain/mod.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/rutabaga_gfx/src/cross_domain/mod.rs b/src/rutabaga_gfx/src/cross_domain/mod.rs index 511b978d1..5a36f081e 100644 --- a/src/rutabaga_gfx/src/cross_domain/mod.rs +++ b/src/rutabaga_gfx/src/cross_domain/mod.rs @@ -1122,15 +1122,27 @@ impl RutabagaContext for CrossDomainContext { .remove(&item_id) .ok_or(RutabagaError::InvalidCrossDomainItemId)?; + fn access_mode(descriptor: &SafeDescriptor) -> RutabagaResult { + Ok( + match fcntl(descriptor.as_fd(), FcntlArg::F_GETFL)? & libc::O_ACCMODE { + libc::O_RDWR => RUTABAGA_MAP_ACCESS_RW, + libc::O_WRONLY => RUTABAGA_MAP_ACCESS_WRITE, + _ => RUTABAGA_MAP_ACCESS_READ, + // (there is a "secret fourth option" O_WRONLY|O_RDWR meaning "no access", very unlikely we'd see it) + }, + ) + } + // Items that are removed from the table after one usage. match item { CrossDomainItem::ShmBlob(descriptor) => { #[allow(unused_mut)] - let mut access = RUTABAGA_MAP_ACCESS_RW; + let mut access = access_mode(&descriptor)?; + // Some compositors actually do send descriptors that are O_RDWR but write-sealed (!) #[cfg(target_os = "linux")] if fcntl(&descriptor, FcntlArg::F_GET_SEALS)? & libc::F_SEAL_WRITE != 0 { access &= !RUTABAGA_MAP_ACCESS_WRITE; - }; + } let hnd = RutabagaHandle { os_handle: descriptor, @@ -1156,10 +1168,7 @@ impl RutabagaContext for CrossDomainContext { }) } CrossDomainItem::DmaBuf(descriptor) => { - let mut access = RUTABAGA_MAP_ACCESS_READ; - if fcntl(descriptor.as_fd(), FcntlArg::F_GETFL)? & libc::O_WRONLY != 0 { - access |= RUTABAGA_MAP_ACCESS_WRITE; - } + let access = access_mode(&descriptor)?; let hnd = RutabagaHandle { os_handle: descriptor,