7
7
8
8
use crate :: error:: SwResultExt ;
9
9
use crate :: { Rect , SoftBufferError } ;
10
- use libc:: { shmat, shmctl, shmdt, shmget, IPC_PRIVATE , IPC_RMID } ;
11
10
use raw_window_handle:: { XcbDisplayHandle , XcbWindowHandle , XlibDisplayHandle , XlibWindowHandle } ;
12
- use std :: ptr :: { null_mut , NonNull } ;
11
+ use rustix :: { fd , mm , shm as posix_shm } ;
13
12
use std:: {
14
- fmt, io, mem,
13
+ fmt,
14
+ fs:: File ,
15
+ io, mem,
15
16
num:: { NonZeroU16 , NonZeroU32 } ,
17
+ ptr:: { null_mut, NonNull } ,
16
18
rc:: Rc ,
17
19
slice,
18
20
} ;
@@ -606,7 +608,8 @@ impl ShmBuffer {
606
608
) -> Result < ( ) , PushBufferError > {
607
609
// Register the guard.
608
610
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 ( ) ;
610
613
611
614
// Take out the old one and detach it.
612
615
if let Some ( ( old_seg, old_id) ) = self . seg . replace ( ( seg, new_id) ) {
@@ -643,7 +646,7 @@ impl ShmBuffer {
643
646
}
644
647
645
648
struct ShmSegment {
646
- id : i32 ,
649
+ id : File ,
647
650
ptr : NonNull < i8 > ,
648
651
size : usize ,
649
652
buffer_size : usize ,
@@ -654,32 +657,40 @@ impl ShmSegment {
654
657
fn new ( size : usize , buffer_size : usize ) -> io:: Result < Self > {
655
658
assert ! ( size >= buffer_size) ;
656
659
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 ( ) ?) ;
663
662
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 ) ?;
675
665
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 ( ) ,
679
670
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
+ } )
683
694
}
684
695
685
696
/// Get this shared memory segment as a reference.
@@ -715,21 +726,19 @@ impl ShmSegment {
715
726
fn size ( & self ) -> usize {
716
727
self . size
717
728
}
729
+ }
718
730
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 ( )
722
734
}
723
735
}
724
736
725
737
impl Drop for ShmSegment {
726
738
fn drop ( & mut self ) {
727
739
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 ( ) ;
733
742
}
734
743
}
735
744
}
@@ -758,6 +767,45 @@ impl Drop for X11Impl {
758
767
}
759
768
}
760
769
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
+
761
809
/// Test to see if SHM is available.
762
810
fn is_shm_available ( c : & impl Connection ) -> bool {
763
811
// Create a small SHM segment.
@@ -773,7 +821,7 @@ fn is_shm_available(c: &impl Connection) -> bool {
773
821
} ;
774
822
775
823
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 ) ;
777
825
let detach = c. shm_detach ( seg_id) ;
778
826
779
827
match ( attach, detach) {
0 commit comments