Skip to content

Commit c0e8723

Browse files
authored
x11: Use POSIX shared memory in X11 backend
The X11 backend used System-V shared memory to communicate shared buffers to the server, but rustix does not support SysV SHM so we had to use libc in this backend. However, there is an alternate X11 API which uses POSIX shared memory, which rustix does support. This PR switches the X11 backend to POSIX shared memory and purges the previous usages of libc. The goal is to remove libc totally. Therefore this PR also removes the default libc dependency from rustix. Signed-off-by: John Nunley <[email protected]>
1 parent a405e03 commit c0e8723

File tree

3 files changed

+91
-45
lines changed

3 files changed

+91
-45
lines changed

Cargo.toml

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ harness = false
1818

1919
[features]
2020
default = ["kms", "x11", "x11-dlopen", "wayland", "wayland-dlopen"]
21-
kms = ["bytemuck", "drm", "libc", "rustix"]
21+
kms = ["bytemuck", "drm", "rustix"]
2222
wayland = ["wayland-backend", "wayland-client", "memmap2", "rustix", "fastrand"]
2323
wayland-dlopen = ["wayland-sys/dlopen"]
24-
x11 = ["as-raw-xcb-connection", "bytemuck", "libc", "rustix", "tiny-xlib", "x11rb"]
24+
x11 = ["as-raw-xcb-connection", "bytemuck", "fastrand", "rustix", "tiny-xlib", "x11rb"]
2525
x11-dlopen = ["tiny-xlib/dlopen", "x11rb/dl-libxcb"]
2626

2727
[dependencies]
@@ -32,18 +32,15 @@ raw-window-handle = "0.5.0"
3232
as-raw-xcb-connection = { version = "1.0.0", optional = true }
3333
bytemuck = { version = "1.12.3", optional = true }
3434
drm = { version = "0.10.0", default-features = false, optional = true }
35+
fastrand = { version = "2.0.0", optional = true }
3536
memmap2 = { version = "0.9.0", optional = true }
36-
libc = { version = "0.2.149", optional = true }
37-
rustix = { version = "0.38.19", features = ["fs", "mm", "shm"], optional = true }
37+
rustix = { version = "0.38.19", features = ["fs", "mm", "shm", "std"], default-features = false, optional = true }
3838
tiny-xlib = { version = "0.2.1", optional = true }
3939
wayland-backend = { version = "0.3.0", features = ["client_system"], optional = true }
4040
wayland-client = { version = "0.31.0", optional = true }
4141
wayland-sys = "0.31.0"
4242
x11rb = { version = "0.12.0", features = ["allow-unsafe-code", "shm"], optional = true }
4343

44-
[target.'cfg(all(unix, not(any(target_vendor = "apple", target_os = "android", target_os = "redox", target_os = "linux", target_os = "freebsd"))))'.dependencies]
45-
fastrand = { version = "2.0.0", optional = true }
46-
4744
[target.'cfg(target_os = "windows")'.dependencies.windows-sys]
4845
version = "0.48.0"
4946
features = ["Win32_Graphics_Gdi", "Win32_UI_WindowsAndMessaging", "Win32_Foundation"]

src/kms.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,8 @@ impl BufferImpl<'_> {
333333
// returns `ENOSYS` and check that before allocating the above and running this.
334334
match self.display.dirty_framebuffer(self.front_fb, &rectangles) {
335335
Ok(()) => {}
336-
Err(drm::SystemError::Unknown { errno }) if errno as i32 == libc::ENOSYS => {}
336+
Err(drm::SystemError::Unknown { errno })
337+
if errno as i32 == rustix::io::Errno::NOSYS.raw_os_error() => {}
337338
Err(e) => {
338339
return Err(SoftBufferError::PlatformError(
339340
Some("failed to dirty framebuffer".into()),

src/x11.rs

Lines changed: 85 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77

88
use crate::error::SwResultExt;
99
use crate::{Rect, SoftBufferError};
10-
use libc::{shmat, shmctl, shmdt, shmget, IPC_PRIVATE, IPC_RMID};
1110
use raw_window_handle::{XcbDisplayHandle, XcbWindowHandle, XlibDisplayHandle, XlibWindowHandle};
12-
use std::ptr::{null_mut, NonNull};
11+
use rustix::{fd, mm, shm as posix_shm};
1312
use std::{
14-
fmt, io, mem,
13+
fmt,
14+
fs::File,
15+
io, mem,
1516
num::{NonZeroU16, NonZeroU32},
17+
ptr::{null_mut, NonNull},
1618
rc::Rc,
1719
slice,
1820
};
@@ -606,7 +608,8 @@ impl ShmBuffer {
606608
) -> Result<(), PushBufferError> {
607609
// Register the guard.
608610
let new_id = conn.generate_id()?;
609-
conn.shm_attach(new_id, seg.id(), true)?.ignore_error();
611+
conn.shm_attach_fd(new_id, fd::AsRawFd::as_raw_fd(&seg), true)?
612+
.ignore_error();
610613

611614
// Take out the old one and detach it.
612615
if let Some((old_seg, old_id)) = self.seg.replace((seg, new_id)) {
@@ -643,7 +646,7 @@ impl ShmBuffer {
643646
}
644647

645648
struct ShmSegment {
646-
id: i32,
649+
id: File,
647650
ptr: NonNull<i8>,
648651
size: usize,
649652
buffer_size: usize,
@@ -654,32 +657,40 @@ impl ShmSegment {
654657
fn new(size: usize, buffer_size: usize) -> io::Result<Self> {
655658
assert!(size >= buffer_size);
656659

657-
unsafe {
658-
// Create the shared memory segment.
659-
let id = shmget(IPC_PRIVATE, size, 0o600);
660-
if id == -1 {
661-
return Err(io::Error::last_os_error());
662-
}
660+
// Create a shared memory segment.
661+
let id = File::from(create_shm_id()?);
663662

664-
// Map the SHM to our memory space.
665-
let ptr = {
666-
let ptr = shmat(id, null_mut(), 0);
667-
match NonNull::new(ptr as *mut i8) {
668-
Some(ptr) => ptr,
669-
None => {
670-
shmctl(id, IPC_RMID, null_mut());
671-
return Err(io::Error::last_os_error());
672-
}
673-
}
674-
};
663+
// Set its length.
664+
id.set_len(size as u64)?;
675665

676-
Ok(Self {
677-
id,
678-
ptr,
666+
// Map the shared memory to our file descriptor space.
667+
let ptr = unsafe {
668+
let ptr = mm::mmap(
669+
null_mut(),
679670
size,
680-
buffer_size,
681-
})
682-
}
671+
mm::ProtFlags::READ | mm::ProtFlags::WRITE,
672+
mm::MapFlags::SHARED,
673+
&id,
674+
0,
675+
)?;
676+
677+
match NonNull::new(ptr.cast()) {
678+
Some(ptr) => ptr,
679+
None => {
680+
return Err(io::Error::new(
681+
io::ErrorKind::Other,
682+
"unexpected null when mapping SHM segment",
683+
));
684+
}
685+
}
686+
};
687+
688+
Ok(Self {
689+
id,
690+
ptr,
691+
size,
692+
buffer_size,
693+
})
683694
}
684695

685696
/// Get this shared memory segment as a reference.
@@ -715,21 +726,19 @@ impl ShmSegment {
715726
fn size(&self) -> usize {
716727
self.size
717728
}
729+
}
718730

719-
/// Get the shared memory ID.
720-
fn id(&self) -> u32 {
721-
self.id as _
731+
impl fd::AsRawFd for ShmSegment {
732+
fn as_raw_fd(&self) -> fd::RawFd {
733+
self.id.as_raw_fd()
722734
}
723735
}
724736

725737
impl Drop for ShmSegment {
726738
fn drop(&mut self) {
727739
unsafe {
728-
// Detach the shared memory segment.
729-
shmdt(self.ptr.as_ptr() as _);
730-
731-
// Delete the shared memory segment.
732-
shmctl(self.id, IPC_RMID, null_mut());
740+
// Unmap the shared memory segment.
741+
mm::munmap(self.ptr.as_ptr().cast(), self.size).ok();
733742
}
734743
}
735744
}
@@ -758,6 +767,45 @@ impl Drop for X11Impl {
758767
}
759768
}
760769

770+
/// Create a shared memory identifier.
771+
fn create_shm_id() -> io::Result<fd::OwnedFd> {
772+
use posix_shm::{Mode, ShmOFlags};
773+
774+
let mut rng = fastrand::Rng::new();
775+
let mut name = String::with_capacity(23);
776+
777+
// Only try four times; the chances of a collision on this space is astronomically low, so if
778+
// we miss four times in a row we're probably under attack.
779+
for i in 0..4 {
780+
name.clear();
781+
name.push_str("softbuffer-x11-");
782+
name.extend(std::iter::repeat_with(|| rng.alphanumeric()).take(7));
783+
784+
// Try to create the shared memory segment.
785+
match posix_shm::shm_open(
786+
&name,
787+
ShmOFlags::RDWR | ShmOFlags::CREATE | ShmOFlags::EXCL,
788+
Mode::RWXU,
789+
) {
790+
Ok(id) => {
791+
posix_shm::shm_unlink(&name).ok();
792+
return Ok(id);
793+
}
794+
795+
Err(rustix::io::Errno::EXIST) => {
796+
log::warn!("x11: SHM ID collision at {} on try number {}", name, i);
797+
}
798+
799+
Err(e) => return Err(e.into()),
800+
};
801+
}
802+
803+
Err(io::Error::new(
804+
io::ErrorKind::Other,
805+
"failed to generate a non-existent SHM name",
806+
))
807+
}
808+
761809
/// Test to see if SHM is available.
762810
fn is_shm_available(c: &impl Connection) -> bool {
763811
// Create a small SHM segment.
@@ -773,7 +821,7 @@ fn is_shm_available(c: &impl Connection) -> bool {
773821
};
774822

775823
let (attach, detach) = {
776-
let attach = c.shm_attach(seg_id, seg.id(), false);
824+
let attach = c.shm_attach_fd(seg_id, fd::AsRawFd::as_raw_fd(&seg), false);
777825
let detach = c.shm_detach(seg_id);
778826

779827
match (attach, detach) {

0 commit comments

Comments
 (0)