1
1
use std:: cell:: RefCell ;
2
+ use std:: ptr:: NonNull ;
2
3
use std:: rc:: Rc ;
3
4
4
5
use ipc_channel:: ipc;
5
- use nix:: sys:: { ptrace, signal} ;
6
+ use nix:: sys:: { mman , ptrace, signal} ;
6
7
use nix:: unistd;
7
8
use rustc_const_eval:: interpret:: InterpResult ;
8
9
@@ -44,6 +45,16 @@ impl Supervisor {
44
45
SUPERVISOR . lock ( ) . unwrap ( ) . is_some ( )
45
46
}
46
47
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
+
47
58
/// Performs an arbitrary FFI call, enabling tracing from the supervisor.
48
59
/// As this locks the supervisor via a mutex, no other threads may enter FFI
49
60
/// until this function returns.
@@ -60,47 +71,67 @@ impl Supervisor {
60
71
61
72
// Get pointers to all the pages the supervisor must allow accesses in
62
73
// 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 ( ) ;
64
85
let raw_stack_ptr: * mut [ u8 ; CALLBACK_STACK_SIZE ] =
65
86
Box :: leak ( Box :: new ( [ 0u8 ; CALLBACK_STACK_SIZE ] ) ) . as_mut_ptr ( ) . cast ( ) ;
66
87
let stack_ptr = raw_stack_ptr. expose_provenance ( ) ;
67
88
let start_info = StartFfiInfo { page_ptrs, stack_ptr } ;
68
89
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 ( ) ;
76
99
}
77
- }
78
100
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
+ } ) ;
104
135
105
136
// SAFETY: Caller upholds that this pointer was allocated as a box with
106
137
// this type.
0 commit comments