xref: /aosp_15_r20/external/crosvm/tube_transporter/src/lib.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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