1 // Copyright 2022 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Do nothing on unix as tube_transporter is windows only. 6 #![cfg(windows)] 7 8 //! This IPC crate is used by the broker process to send boot data across the 9 //! different crosvm child processes on Windows. 10 11 use std::fmt; 12 use std::fmt::Display; 13 14 use base::deserialize_and_recv; 15 use base::named_pipes::BlockingMode; 16 use base::named_pipes::FramingMode; 17 use base::named_pipes::PipeConnection; 18 use base::serialize_and_send; 19 use base::set_alias_pid; 20 use base::set_duplicate_handle_tube; 21 use base::DuplicateHandleTube; 22 use base::FromRawDescriptor; 23 use base::RawDescriptor; 24 use base::Tube; 25 use base::TubeError; 26 use serde::Deserialize; 27 use serde::Serialize; 28 use thiserror::Error as ThisError; 29 30 pub mod packed_tube; 31 32 pub type TransportTubeResult<T> = std::result::Result<T, TubeTransportError>; 33 34 /// Contains information for a child process to set up the Tube for use. 35 #[derive(Serialize, Deserialize, Debug)] 36 pub struct TubeTransferData { 37 // Tube to be sent to the child process. 38 pub tube: Tube, 39 // Used to determine what the Tube's purpose is. 40 pub tube_token: TubeToken, 41 } 42 43 #[derive(Debug, ThisError)] 44 pub enum TubeTransportError { 45 #[error("Serializing and sending failed: {0}")] 46 SerializeSendError(TubeError), 47 #[error("Serializing and recving failed: {0}")] 48 DeserializeRecvError(TubeError), 49 #[error("Tube with token {0} not found")] 50 TubeNotFound(TubeToken), 51 } 52 53 /// The target child process will use this decide what a Tube's purpose is. 54 #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] 55 pub enum TubeToken { 56 Bootstrap, 57 Control, 58 Ipc, 59 VhostUser, 60 } 61 62 impl Display for TubeToken { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 64 write!(f, "{:?}", self) 65 } 66 } 67 68 #[derive(Serialize, Deserialize, Debug)] 69 pub struct TransportData { 70 dh_tube: Option<Tube>, 71 alias_pid: Option<u32>, 72 // A list Tubes and related metadata to transfer. This list should not be emptied, since 73 // that would cause a Tube to drop and thus closing the Tube. 74 tube_transfer_data_list: Vec<TubeTransferData>, 75 } 76 77 /// Used by the Broker process to transport Tubes to a target child process. 78 pub struct TubeTransporter { 79 pipe_connection: PipeConnection, 80 transport_data: TransportData, 81 } 82 83 impl TubeTransporter { 84 /// WARNING: PipeConnection must be a message mode, blocking pipe. new( pipe_connection: PipeConnection, tube_transfer_data_list: Vec<TubeTransferData>, alias_pid: Option<u32>, dh_tube: Option<Tube>, ) -> TubeTransporter85 pub fn new( 86 pipe_connection: PipeConnection, 87 tube_transfer_data_list: Vec<TubeTransferData>, 88 alias_pid: Option<u32>, 89 dh_tube: Option<Tube>, 90 ) -> TubeTransporter { 91 TubeTransporter { 92 pipe_connection, 93 transport_data: TransportData { 94 dh_tube, 95 alias_pid, 96 tube_transfer_data_list, 97 }, 98 } 99 } 100 101 /// Sends tubes to the other end of the pipe. Note that you must provide the destination 102 /// PID so that descriptors can be sent. serialize_and_transport(&self, child_pid: u32) -> TransportTubeResult<()>103 pub fn serialize_and_transport(&self, child_pid: u32) -> TransportTubeResult<()> { 104 serialize_and_send( 105 |buf| self.pipe_connection.write(buf), 106 &self.transport_data, 107 /* target_pid= */ Some(child_pid), 108 ) 109 .map_err(TubeTransportError::SerializeSendError)?; 110 Ok(()) 111 } 112 push_tube(&mut self, tube: Tube, tube_token: TubeToken)113 pub fn push_tube(&mut self, tube: Tube, tube_token: TubeToken) { 114 self.transport_data 115 .tube_transfer_data_list 116 .push(TubeTransferData { tube, tube_token }); 117 } 118 } 119 120 /// Used by the child process to read Tubes sent from the Broker. 121 pub struct TubeTransporterReader { 122 reader_pipe_connection: PipeConnection, 123 } 124 125 impl TubeTransporterReader { 126 /// WARNING: PipeConnection must be a message mode, blocking pipe. create_tube_transporter_reader(pipe_connection: PipeConnection) -> Self127 pub fn create_tube_transporter_reader(pipe_connection: PipeConnection) -> Self { 128 TubeTransporterReader { 129 reader_pipe_connection: pipe_connection, 130 } 131 } 132 read_tubes(&self) -> TransportTubeResult<TubeTransferDataList>133 pub fn read_tubes(&self) -> TransportTubeResult<TubeTransferDataList> { 134 let res: TransportData = 135 // SAFETY: We provide a valid buffer to `read()` 136 deserialize_and_recv(|buf| unsafe { self.reader_pipe_connection.read(buf) }) 137 .map_err(TubeTransportError::DeserializeRecvError)?; 138 139 if let Some(tube) = res.dh_tube { 140 let dh_tube = DuplicateHandleTube::new(tube); 141 set_duplicate_handle_tube(dh_tube); 142 } 143 if let Some(alias_pid) = res.alias_pid { 144 set_alias_pid(alias_pid); 145 } 146 Ok(TubeTransferDataList(res.tube_transfer_data_list)) 147 } 148 } 149 150 impl FromRawDescriptor for TubeTransporterReader { 151 /// Creates a TubeTransporterReader from a raw descriptor. 152 /// # Safety 153 /// 1. descriptor is valid & ownership is released to the TubeTransporterReader 154 /// 155 /// # Avoiding U.B. 156 /// 1. The underlying pipe is a message pipe in wait mode. from_raw_descriptor(descriptor: RawDescriptor) -> Self157 unsafe fn from_raw_descriptor(descriptor: RawDescriptor) -> Self { 158 TubeTransporterReader::create_tube_transporter_reader(PipeConnection::from_raw_descriptor( 159 descriptor, 160 FramingMode::Message, 161 BlockingMode::Wait, 162 )) 163 } 164 } 165 166 #[derive(Debug)] 167 pub struct TubeTransferDataList(Vec<TubeTransferData>); 168 169 impl TubeTransferDataList { get_tube(&mut self, token: TubeToken) -> TransportTubeResult<Tube>170 pub fn get_tube(&mut self, token: TubeToken) -> TransportTubeResult<Tube> { 171 Ok(self 172 .0 173 .remove( 174 match self 175 .0 176 .iter() 177 .position(|tube_data| tube_data.tube_token == token) 178 { 179 Some(pos) => pos, 180 None => return Err(TubeTransportError::TubeNotFound(token)), 181 }, 182 ) 183 .tube) 184 } 185 } 186 187 #[cfg(test)] 188 mod tests { 189 use std::thread; 190 191 use base::named_pipes::pair; 192 use base::named_pipes::BlockingMode; 193 use base::named_pipes::FramingMode; 194 use base::Event; 195 use base::EventWaitResult; 196 use winapi::um::processthreadsapi::GetCurrentProcessId; 197 198 use super::*; 199 200 #[test] test_send_tubes_through_tube_transporter()201 fn test_send_tubes_through_tube_transporter() { 202 let (broker_pipe_connection_server, child_process_pipe) = pair( 203 &FramingMode::Message, 204 &BlockingMode::Wait, 205 /* timeout= */ 0, 206 ) 207 .unwrap(); 208 209 // Start a thread to simulate a new process. This thread will attempt to read the Tubes 210 // from the pipe. 211 let child_join_handle = thread::Builder::new() 212 .name("Sandboxed child process listening thread".to_string()) 213 .spawn(move || { 214 let child_process_pipe = child_process_pipe; 215 let transporter_reader = 216 TubeTransporterReader::create_tube_transporter_reader(child_process_pipe); 217 transporter_reader.read_tubes().unwrap() 218 }) 219 .unwrap(); 220 221 // SAFETY: This kernel function just returns the current PID. 222 // 223 // We want to get the current PID as a sanity check for the `OpenProcess` call 224 // when duplicating a handle through a Tube. 225 let current_pid = unsafe { GetCurrentProcessId() }; 226 227 // We want the test to drop device_tube_1 and 2 after transportation is complete. Since 228 // Tubes have many SafeDescriptors, we still want Tubes to work even if their original 229 // handles are closed. 230 let (main_tube_1, main_tube_2) = { 231 let (main_tube_1, device_tube_1) = Tube::pair().unwrap(); 232 let (main_tube_2, device_tube_2) = Tube::pair().unwrap(); 233 234 let tube_transporter = TubeTransporter::new( 235 broker_pipe_connection_server, 236 vec![ 237 TubeTransferData { 238 tube: device_tube_1, 239 tube_token: TubeToken::Control, 240 }, 241 TubeTransferData { 242 tube: device_tube_2, 243 tube_token: TubeToken::Ipc, 244 }, 245 ], 246 /* alias_pid= */ 247 None, 248 /* dh_tube= */ 249 None, 250 ); 251 252 // TODO: we just test within the same process here, so we send to ourselves. 253 tube_transporter 254 .serialize_and_transport(current_pid) 255 .expect("serialize and transporting failed"); 256 257 (main_tube_1, main_tube_2) 258 }; 259 260 let tube_data_list = child_join_handle.join().unwrap().0; 261 assert_eq!(tube_data_list.len(), 2); 262 assert_eq!(tube_data_list[0].tube_token, TubeToken::Control); 263 assert_eq!(tube_data_list[1].tube_token, TubeToken::Ipc); 264 265 // Test sending a string through the Tubes 266 tube_data_list[0] 267 .tube 268 .send(&"hello main 1".to_string()) 269 .expect("tube 1 failed to send"); 270 tube_data_list[1] 271 .tube 272 .send(&"hello main 2".to_string()) 273 .expect("tube 2 failed to send."); 274 275 assert_eq!(main_tube_1.recv::<String>().unwrap(), "hello main 1"); 276 assert_eq!(main_tube_2.recv::<String>().unwrap(), "hello main 2"); 277 278 // Test sending a handle through a Tube. Note that the Tube in `tube_data_list[1]` can't 279 // send a handle across because `CHILD_PID` isn't mapped to a real process. 280 let event_handle = Event::new().unwrap(); 281 282 tube_data_list[0] 283 .tube 284 .send(&event_handle) 285 .expect("tube 1 failed to send"); 286 287 let duped_handle = main_tube_1.recv::<Event>().unwrap(); 288 289 event_handle.signal().unwrap(); 290 291 assert!(matches!( 292 duped_handle 293 .wait_timeout(std::time::Duration::from_millis(2000)) 294 .unwrap(), 295 EventWaitResult::Signaled 296 )); 297 } 298 } 299