devices/virtio/vhost_user_backend/fs/sys/
linux.rs1use std::os::fd::OwnedFd;
6use std::os::unix::process::ExitStatusExt;
7use std::path::Path;
8use std::path::PathBuf;
9
10use anyhow::bail;
11use anyhow::Context;
12use base::error;
13use base::linux::max_open_files;
14use base::sys::wait_for_pid;
15use base::AsRawDescriptor;
16use base::AsRawDescriptors;
17use base::IntoRawDescriptor;
18use base::RawDescriptor;
19use base::UnixSeqpacketListener;
20use cros_async::Executor;
21use jail::create_base_minijail;
22use jail::create_base_minijail_without_pivot_root;
23use minijail::Minijail;
24
25use crate::virtio::vhost_user_backend::fs::FsBackend;
26use crate::virtio::vhost_user_backend::fs::Options;
27use crate::virtio::vhost_user_backend::handler::DeviceRequestHandler;
28use crate::virtio::vhost_user_backend::BackendConnection;
29
30fn default_uidmap() -> String {
31 let euid = unsafe { libc::geteuid() };
33 format!("{euid} {euid} 1")
34}
35
36fn default_gidmap() -> String {
37 let egid = unsafe { libc::getegid() };
39 format!("{egid} {egid} 1")
40}
41
42#[allow(clippy::unnecessary_cast)]
43fn jail_and_fork(
44 mut keep_rds: Vec<RawDescriptor>,
45 dir_path: PathBuf,
46 uid: u32,
47 gid: u32,
48 uid_map: Option<String>,
49 gid_map: Option<String>,
50 disable_sandbox: bool,
51 pivot_root: bool,
52) -> anyhow::Result<i32> {
53 let limit = max_open_files()
54 .context("failed to get max open files")?
55 .rlim_max;
56 let jail = if disable_sandbox {
58 if pivot_root {
59 create_base_minijail(dir_path.as_path(), limit)
60 } else {
61 create_base_minijail_without_pivot_root(dir_path.as_path(), limit)
62 }?
63 } else {
64 let mut j: Minijail = Minijail::new()?;
65
66 j.namespace_pids();
67 j.namespace_user();
68 j.namespace_user_disable_setgroups();
69 if uid != 0 {
70 j.change_uid(uid);
71 }
72 if gid != 0 {
73 j.change_gid(gid);
74 }
75 j.uidmap(&uid_map.unwrap_or_else(default_uidmap))?;
76 j.gidmap(&gid_map.unwrap_or_else(default_gidmap))?;
77 j.run_as_init();
78
79 j.namespace_vfs();
80 j.namespace_net();
81 j.no_new_privs();
82
83 if dir_path != Path::new("/") {
85 j.namespace_vfs();
87 j.enter_pivot_root(&dir_path)?;
88 }
89 j.set_remount_mode(libc::MS_SLAVE);
90
91 j.set_rlimit(libc::RLIMIT_NOFILE as i32, limit, limit)?;
92 j.set_rlimit(libc::RLIMIT_MEMLOCK as i32, 1 << 20, 1 << 20)?;
94 #[cfg(not(feature = "seccomp_trace"))]
95 jail::set_embedded_bpf_program(&mut j, "fs_device_vhost_user")?;
96 j.use_seccomp_filter();
97 j
98 };
99
100 keep_rds.sort_unstable();
102 keep_rds.dedup();
103
104 let pid = unsafe { jail.fork(Some(&keep_rds))? };
107
108 if pid > 0 {
109 unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) };
113 }
114
115 if pid < 0 {
116 bail!("Fork error! {}", std::io::Error::last_os_error());
117 }
118
119 Ok(pid)
120}
121
122#[allow(unused_mut)]
125pub fn start_device(mut opts: Options) -> anyhow::Result<()> {
126 #[allow(unused_mut)]
127 let mut is_pivot_root_required = true;
128 #[cfg(feature = "fs_runtime_ugid_map")]
129 if let Some(ref mut cfg) = opts.cfg {
130 if !cfg.ugid_map.is_empty() && (!opts.disable_sandbox || !opts.skip_pivot_root) {
131 bail!("uid_gid_map can only be set with disable sandbox and skip_pivot_root option");
132 }
133
134 if cfg.unmap_guest_memory_on_fork && !opts.disable_sandbox {
135 bail!("unmap_guest_memory_on_fork requires --disable-sandbox");
136 }
137
138 if opts.skip_pivot_root {
139 is_pivot_root_required = false;
140 }
141 }
142 let ex = Executor::new().context("Failed to create executor")?;
143
144 let mut allowlist_listener_fd = None;
145 if let Some(ref path) = opts.allowlist_socket_path {
146 let listener =
147 UnixSeqpacketListener::bind(path).context("failed to bind allowlist socket")?;
148 let fd = OwnedFd::from(listener).into_raw_descriptor();
149 allowlist_listener_fd = Some(fd);
150 }
151
152 let mut fs_device = FsBackend::new(
153 &opts.tag,
154 opts.shared_dir
155 .to_str()
156 .expect("Failed to convert opts.shared_dir to str()"),
157 opts.skip_pivot_root,
158 opts.cfg,
159 allowlist_listener_fd,
160 )?;
161
162 let mut keep_rds = fs_device.keep_rds.clone();
163 keep_rds.append(&mut ex.as_raw_descriptors());
164
165 let conn =
166 BackendConnection::from_opts(opts.socket.as_deref(), opts.socket_path.as_deref(), opts.fd)?;
167 keep_rds.push(conn.as_raw_descriptor());
168 if let Some(fd) = allowlist_listener_fd {
169 keep_rds.push(fd);
170 }
171
172 base::syslog::push_descriptors(&mut keep_rds);
173 cros_tracing::push_descriptors!(&mut keep_rds);
174 metrics::push_descriptors(&mut keep_rds);
175 let pid = jail_and_fork(
176 keep_rds,
177 opts.shared_dir,
178 opts.uid,
179 opts.gid,
180 opts.uid_map,
181 opts.gid_map,
182 opts.disable_sandbox,
183 is_pivot_root_required,
184 )?;
185
186 match pid {
187 0 => {
188 fs_device.start_allowlist_listener();
190 let mut handler = DeviceRequestHandler::new(fs_device);
194 if let Err(e) = ex.run_until(conn.run_req_handler(&mut handler, &ex)) {
195 error!("Error in vhost-user-fs device: {:#}", e);
196 std::process::exit(1);
197 }
198 std::process::exit(0);
199 }
200 pid if pid < 0 => {
201 unreachable!("fork error must have been handled in jail_and_fork()");
202 }
203 _ => {
204 drop(fs_device);
206
207 let (_child_pid, status) =
208 wait_for_pid(pid, 0).context("failed to wait for child process")?;
209 if let Some(signal) = status.signal() {
210 panic!("Child process {pid} was killed by signal {signal}");
211 }
212 if let Some(exit_code) = status.code() {
213 if exit_code != 0 {
214 bail!("Child process {pid} exited with code {exit_code}");
215 }
216 }
217 }
218 };
219 Ok(())
220}