From 7bf94cc9726fc9086ce5bb39a696fb6583eda9ef 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 7f11a1bb529aec78bd2f2b714462211a84525b52 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,