11use std:: cell:: RefCell ;
2+ use std:: ptr:: NonNull ;
23use std:: rc:: Rc ;
34
45use ipc_channel:: ipc;
5- use nix:: sys:: { ptrace, signal} ;
6+ use nix:: sys:: { mman , ptrace, signal} ;
67use nix:: unistd;
78use rustc_const_eval:: interpret:: InterpResult ;
89
@@ -44,6 +45,16 @@ impl Supervisor {
4445 SUPERVISOR . lock ( ) . unwrap ( ) . is_some ( )
4546 }
4647
48+ unsafe fn protect_pages (
49+ pages : impl Iterator < Item = ( NonNull < u8 > , usize ) > ,
50+ prot : mman:: ProtFlags ,
51+ ) -> Result < ( ) , nix:: errno:: Errno > {
52+ for ( pg, sz) in pages {
53+ unsafe { mman:: mprotect ( pg. cast ( ) , sz, prot) ? } ;
54+ }
55+ Ok ( ( ) )
56+ }
57+
4758 /// Performs an arbitrary FFI call, enabling tracing from the supervisor.
4859 /// As this locks the supervisor via a mutex, no other threads may enter FFI
4960 /// until this function returns.
@@ -60,47 +71,67 @@ impl Supervisor {
6071
6172 // Get pointers to all the pages the supervisor must allow accesses in
6273 // and prepare the callback stack.
63- let page_ptrs = alloc. borrow ( ) . pages ( ) . collect ( ) ;
74+ let alloc = alloc. borrow ( ) ;
75+ let page_size = alloc. page_size ( ) ;
76+ let page_ptrs = alloc
77+ . pages ( )
78+ . flat_map ( |( pg, sz) | {
79+ // Convert (page, size) pair into list of pages.
80+ let start = pg. expose_provenance ( ) . get ( ) ;
81+ ( 0 ..sz. strict_div ( alloc. page_size ( ) ) )
82+ . map ( move |i| start. strict_add ( i. strict_mul ( page_size) ) )
83+ } )
84+ . collect ( ) ;
6485 let raw_stack_ptr: * mut [ u8 ; CALLBACK_STACK_SIZE ] =
6586 Box :: leak ( Box :: new ( [ 0u8 ; CALLBACK_STACK_SIZE ] ) ) . as_mut_ptr ( ) . cast ( ) ;
6687 let stack_ptr = raw_stack_ptr. expose_provenance ( ) ;
6788 let start_info = StartFfiInfo { page_ptrs, stack_ptr } ;
6889
69- // SAFETY: We do not access machine memory past this point until the
70- // supervisor is ready to allow it.
71- unsafe {
72- if alloc. borrow_mut ( ) . start_ffi ( ) . is_err ( ) {
73- // Don't mess up unwinding by maybe leaving the memory partly protected
74- alloc. borrow_mut ( ) . end_ffi ( ) ;
75- panic ! ( "Cannot protect memory for FFI call!" ) ;
90+ // Unwinding might be messed up due to partly protected memory, so let's abort if something
91+ // breaks inside here.
92+ let res = std:: panic:: abort_unwind ( || {
93+ // SAFETY: We do not access machine memory past this point until the
94+ // supervisor is ready to allow it.
95+ // FIXME: this is sketchy, as technically the memory is still in the Rust Abstract Machine,
96+ // and the compiler would be allowed to reorder accesses below this block...
97+ unsafe {
98+ Self :: protect_pages ( alloc. pages ( ) , mman:: ProtFlags :: PROT_NONE ) . unwrap ( ) ;
7699 }
77- }
78100
79- // Send over the info.
80- // NB: if we do not wait to receive a blank confirmation response, it is
81- // possible that the supervisor is alerted of the SIGSTOP *before* it has
82- // actually received the start_info, thus deadlocking! This way, we can
83- // enforce an ordering for these events.
84- sv. message_tx . send ( TraceRequest :: StartFfi ( start_info) ) . unwrap ( ) ;
85- sv. confirm_rx . recv ( ) . unwrap ( ) ;
86- // We need to be stopped for the supervisor to be able to make certain
87- // modifications to our memory - simply waiting on the recv() doesn't
88- // count.
89- signal:: raise ( signal:: SIGSTOP ) . unwrap ( ) ;
90-
91- let res = f ( ) ;
92-
93- // We can't use IPC channels here to signal that FFI mode has ended,
94- // since they might allocate memory which could get us stuck in a SIGTRAP
95- // with no easy way out! While this could be worked around, it is much
96- // simpler and more robust to simply use the signals which are left for
97- // arbitrary usage. Since this will block until we are continued by the
98- // supervisor, we can assume past this point that everything is back to
99- // normal.
100- signal:: raise ( signal:: SIGUSR1 ) . unwrap ( ) ;
101-
102- // This is safe! It just sets memory to normal expected permissions.
103- alloc. borrow_mut ( ) . end_ffi ( ) ;
101+ // Send over the info.
102+ // NB: if we do not wait to receive a blank confirmation response, it is
103+ // possible that the supervisor is alerted of the SIGSTOP *before* it has
104+ // actually received the start_info, thus deadlocking! This way, we can
105+ // enforce an ordering for these events.
106+ sv. message_tx . send ( TraceRequest :: StartFfi ( start_info) ) . unwrap ( ) ;
107+ sv. confirm_rx . recv ( ) . unwrap ( ) ;
108+ // We need to be stopped for the supervisor to be able to make certain
109+ // modifications to our memory - simply waiting on the recv() doesn't
110+ // count.
111+ signal:: raise ( signal:: SIGSTOP ) . unwrap ( ) ;
112+
113+ let res = f ( ) ;
114+
115+ // We can't use IPC channels here to signal that FFI mode has ended,
116+ // since they might allocate memory which could get us stuck in a SIGTRAP
117+ // with no easy way out! While this could be worked around, it is much
118+ // simpler and more robust to simply use the signals which are left for
119+ // arbitrary usage. Since this will block until we are continued by the
120+ // supervisor, we can assume past this point that everything is back to
121+ // normal.
122+ signal:: raise ( signal:: SIGUSR1 ) . unwrap ( ) ;
123+
124+ // SAFETY: We set memory back to normal, so this is safe.
125+ unsafe {
126+ Self :: protect_pages (
127+ alloc. pages ( ) ,
128+ mman:: ProtFlags :: PROT_READ | mman:: ProtFlags :: PROT_WRITE ,
129+ )
130+ . unwrap ( ) ;
131+ }
132+
133+ res
134+ } ) ;
104135
105136 // SAFETY: Caller upholds that this pointer was allocated as a box with
106137 // this type.
0 commit comments