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