• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! Runs hardware devices in child processes.
6 
7 use std::fmt::{self, Display};
8 use std::os::unix::io::{AsRawFd, RawFd};
9 use std::process;
10 use std::time::Duration;
11 use std::{self, io};
12 
13 use io_jail::{self, Minijail};
14 use libc::pid_t;
15 use msg_socket::{MsgOnSocket, MsgReceiver, MsgSender, MsgSocket};
16 use sys_util::{error, net::UnixSeqpacket};
17 
18 use crate::BusDevice;
19 
20 /// Errors for proxy devices.
21 #[derive(Debug)]
22 pub enum Error {
23     ForkingJail(io_jail::Error),
24     Io(io::Error),
25 }
26 pub type Result<T> = std::result::Result<T, Error>;
27 
28 impl Display for Error {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result29     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
30         use self::Error::*;
31 
32         match self {
33             ForkingJail(e) => write!(f, "Failed to fork jail process: {}", e),
34             Io(e) => write!(f, "IO error configuring proxy device {}.", e),
35         }
36     }
37 }
38 
39 const SOCKET_TIMEOUT_MS: u64 = 2000;
40 
41 #[derive(MsgOnSocket)]
42 enum Command {
43     Read {
44         len: u32,
45         offset: u64,
46     },
47     Write {
48         len: u32,
49         offset: u64,
50         data: [u8; 8],
51     },
52     ReadConfig(u32),
53     WriteConfig {
54         reg_idx: u32,
55         offset: u32,
56         len: u32,
57         data: [u8; 4],
58     },
59     Shutdown,
60 }
61 
62 #[derive(MsgOnSocket)]
63 enum CommandResult {
64     Ok,
65     ReadResult([u8; 8]),
66     ReadConfigResult(u32),
67 }
68 
child_proc(sock: UnixSeqpacket, device: &mut dyn BusDevice)69 fn child_proc(sock: UnixSeqpacket, device: &mut dyn BusDevice) {
70     let mut running = true;
71     let sock = MsgSocket::<CommandResult, Command>::new(sock);
72 
73     while running {
74         let cmd = match sock.recv() {
75             Ok(cmd) => cmd,
76             Err(err) => {
77                 error!("child device process failed recv: {}", err);
78                 break;
79             }
80         };
81 
82         let res = match cmd {
83             Command::Read { len, offset } => {
84                 let mut buffer = [0u8; 8];
85                 device.read(offset, &mut buffer[0..len as usize]);
86                 sock.send(&CommandResult::ReadResult(buffer))
87             }
88             Command::Write { len, offset, data } => {
89                 let len = len as usize;
90                 device.write(offset, &data[0..len]);
91                 sock.send(&CommandResult::Ok)
92             }
93             Command::ReadConfig(idx) => {
94                 let val = device.config_register_read(idx as usize);
95                 sock.send(&CommandResult::ReadConfigResult(val))
96             }
97             Command::WriteConfig {
98                 reg_idx,
99                 offset,
100                 len,
101                 data,
102             } => {
103                 let len = len as usize;
104                 device.config_register_write(reg_idx as usize, offset as u64, &data[0..len]);
105                 sock.send(&CommandResult::Ok)
106             }
107             Command::Shutdown => {
108                 running = false;
109                 sock.send(&CommandResult::Ok)
110             }
111         };
112         if let Err(e) = res {
113             error!("child device process failed send: {}", e);
114         }
115     }
116 }
117 
118 /// Wraps an inner `BusDevice` that is run inside a child process via fork.
119 ///
120 /// Because forks are very unfriendly to destructors and all memory mappings and file descriptors
121 /// are inherited, this should be used as early as possible in the main process.
122 pub struct ProxyDevice {
123     sock: MsgSocket<Command, CommandResult>,
124     pid: pid_t,
125     debug_label: String,
126 }
127 
128 impl ProxyDevice {
129     /// Takes the given device and isolates it into another process via fork before returning.
130     ///
131     /// The forked process will automatically be terminated when this is dropped, so be sure to keep
132     /// a reference.
133     ///
134     /// # Arguments
135     /// * `device` - The device to isolate to another process.
136     /// * `keep_fds` - File descriptors that will be kept open in the child
new<D: BusDevice>( mut device: D, jail: &Minijail, mut keep_fds: Vec<RawFd>, ) -> Result<ProxyDevice>137     pub fn new<D: BusDevice>(
138         mut device: D,
139         jail: &Minijail,
140         mut keep_fds: Vec<RawFd>,
141     ) -> Result<ProxyDevice> {
142         let debug_label = device.debug_label();
143         let (child_sock, parent_sock) = UnixSeqpacket::pair().map_err(Error::Io)?;
144 
145         keep_fds.push(child_sock.as_raw_fd());
146         // Forking here is safe as long as the program is still single threaded.
147         let pid = unsafe {
148             match jail.fork(Some(&keep_fds)).map_err(Error::ForkingJail)? {
149                 0 => {
150                     device.on_sandboxed();
151                     child_proc(child_sock, &mut device);
152                     // ! Never returns
153                     process::exit(0);
154                 }
155                 p => p,
156             }
157         };
158 
159         parent_sock
160             .set_write_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
161             .map_err(Error::Io)?;
162         parent_sock
163             .set_read_timeout(Some(Duration::from_millis(SOCKET_TIMEOUT_MS)))
164             .map_err(Error::Io)?;
165         Ok(ProxyDevice {
166             sock: MsgSocket::<Command, CommandResult>::new(parent_sock),
167             pid,
168             debug_label,
169         })
170     }
171 
pid(&self) -> pid_t172     pub fn pid(&self) -> pid_t {
173         self.pid
174     }
175 
sync_send(&self, cmd: Command) -> Option<CommandResult>176     fn sync_send(&self, cmd: Command) -> Option<CommandResult> {
177         let res = self.sock.send(&cmd);
178         if let Err(e) = res {
179             error!(
180                 "failed write to child device process {}: {}",
181                 self.debug_label, e,
182             );
183         };
184         match self.sock.recv() {
185             Err(e) => {
186                 error!(
187                     "failed read from child device process {}: {}",
188                     self.debug_label, e,
189                 );
190                 None
191             }
192             Ok(r) => Some(r),
193         }
194     }
195 }
196 
197 impl BusDevice for ProxyDevice {
debug_label(&self) -> String198     fn debug_label(&self) -> String {
199         self.debug_label.clone()
200     }
201 
config_register_write(&mut self, reg_idx: usize, offset: u64, data: &[u8])202     fn config_register_write(&mut self, reg_idx: usize, offset: u64, data: &[u8]) {
203         let len = data.len() as u32;
204         let mut buffer = [0u8; 4];
205         buffer[0..data.len()].clone_from_slice(data);
206         let reg_idx = reg_idx as u32;
207         let offset = offset as u32;
208         self.sync_send(Command::WriteConfig {
209             reg_idx,
210             offset,
211             len,
212             data: buffer,
213         });
214     }
215 
config_register_read(&self, reg_idx: usize) -> u32216     fn config_register_read(&self, reg_idx: usize) -> u32 {
217         let res = self.sync_send(Command::ReadConfig(reg_idx as u32));
218         if let Some(CommandResult::ReadConfigResult(val)) = res {
219             val
220         } else {
221             0
222         }
223     }
224 
read(&mut self, offset: u64, data: &mut [u8])225     fn read(&mut self, offset: u64, data: &mut [u8]) {
226         let len = data.len() as u32;
227         if let Some(CommandResult::ReadResult(buffer)) =
228             self.sync_send(Command::Read { len, offset })
229         {
230             let len = data.len();
231             data.clone_from_slice(&buffer[0..len]);
232         }
233     }
234 
write(&mut self, offset: u64, data: &[u8])235     fn write(&mut self, offset: u64, data: &[u8]) {
236         let mut buffer = [0u8; 8];
237         let len = data.len() as u32;
238         buffer[0..data.len()].clone_from_slice(data);
239         self.sync_send(Command::Write {
240             len,
241             offset,
242             data: buffer,
243         });
244     }
245 }
246 
247 impl Drop for ProxyDevice {
drop(&mut self)248     fn drop(&mut self) {
249         self.sync_send(Command::Shutdown);
250     }
251 }
252