• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 use base::deserialize_and_recv;
6 use base::named_pipes;
7 use base::named_pipes::BlockingMode;
8 use base::named_pipes::FramingMode;
9 use base::serialize_and_send;
10 use base::Error as SysError;
11 use base::FromRawDescriptor;
12 use base::IntoRawDescriptor;
13 use base::PipeConnection;
14 use base::SafeDescriptor;
15 use base::Tube;
16 use base::TubeError;
17 use serde::Deserialize;
18 use serde::Serialize;
19 use thiserror::Error as ThisError;
20 
21 pub type PackedTubeResult<T> = Result<T, PackedTubeError>;
22 
23 #[derive(Debug, ThisError)]
24 pub enum PackedTubeError {
25     #[error("Serializing and recving failed: {0}")]
26     DeserializeRecvError(TubeError),
27     #[error("Named pipe error: {0}")]
28     PipeError(SysError),
29     #[error("Serializing and sending failed: {0}")]
30     SerializeSendError(TubeError),
31 }
32 
33 #[derive(Deserialize, Serialize)]
34 struct PackedTube {
35     tube: Tube,
36     server_pipe: PipeConnection,
37 }
38 
39 /// Sends a [Tube] through a protocol that expects a [RawDescriptor].
40 ///
41 /// A packed tube works by creating a named pipe pair, and serializing both the Tube and the
42 /// server end of the pipe. Then, it returns the client end of the named pipe pair, which can be
43 /// used as the desired descriptor to send / duplicate to the target.
44 ///
45 /// The receiver will need to use [packed_tube::unpack] to read the message off the pipe, and thus
46 /// extract a real [Tube]. It will also read the server end of the pipe, and close it. The
47 /// `receiver_pid` is the pid of the process that will be unpacking the tube.
48 ///
49 /// # Safety
50 /// To prevent dangling handles, the resulting descriptor must be passed to [packed_tube::unpack],
51 /// in the process which corresponds to `receiver_pid`.
pack(tube: Tube, receiver_pid: u32) -> PackedTubeResult<SafeDescriptor>52 pub unsafe fn pack(tube: Tube, receiver_pid: u32) -> PackedTubeResult<SafeDescriptor> {
53     let (server_pipe, client_pipe) = named_pipes::pair(
54         &FramingMode::Message,
55         &BlockingMode::Wait,
56         /* timeout= */ 0,
57     )
58     .map_err(SysError::from)
59     .map_err(PackedTubeError::PipeError)?;
60 
61     let packed = PackedTube { tube, server_pipe };
62 
63     // Serialize the packed tube, which also duplicates the server end of the pipe into the other
64     // process. This lets us drop it on our side without destroying the channel.
65     serialize_and_send(
66         |buf| packed.server_pipe.write(buf),
67         &packed,
68         Some(receiver_pid),
69     )
70     .map_err(PackedTubeError::SerializeSendError)?;
71 
72     Ok(SafeDescriptor::from_raw_descriptor(
73         client_pipe.into_raw_descriptor(),
74     ))
75 }
76 
77 /// Unpacks a tube from a client descriptor. This must come from a packed tube.
78 ///
79 /// # Safety
80 /// The descriptor passed in must come from [packed_tube::pack].
unpack(descriptor: SafeDescriptor) -> PackedTubeResult<Tube>81 pub unsafe fn unpack(descriptor: SafeDescriptor) -> PackedTubeResult<Tube> {
82     let pipe = PipeConnection::from_raw_descriptor(
83         descriptor.into_raw_descriptor(),
84         FramingMode::Message,
85         BlockingMode::Wait,
86     );
87     // Safe because we own the descriptor and it came from a PackedTube.
88     let unpacked: PackedTube = deserialize_and_recv(|buf| pipe.read(buf))
89         .map_err(PackedTubeError::DeserializeRecvError)?;
90     // By dropping `unpacked` we close the server end of the pipe.
91     Ok(unpacked.tube)
92 }
93 
94 #[cfg(test)]
95 mod tests {
96     use base::Tube;
97 
98     use crate::packed_tube;
99 
100     #[test]
101     /// Tests packing and unpacking.
test_pack_unpack()102     fn test_pack_unpack() {
103         let (tube_server, tube_client) = Tube::pair().unwrap();
104         let packed_tube = unsafe { packed_tube::pack(tube_client, std::process::id()).unwrap() };
105 
106         // Safe because get_descriptor clones the underlying pipe.
107         let recovered_tube = unsafe { packed_tube::unpack(packed_tube).unwrap() };
108 
109         let test_message = "Test message".to_string();
110         tube_server.send(&test_message).unwrap();
111         let received: String = recovered_tube.recv().unwrap();
112 
113         assert_eq!(test_message, received);
114     }
115 }
116