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 std::io;
6 use std::path::Path;
7 use std::path::PathBuf;
8
9 use anyhow::bail;
10 use anyhow::Context;
11 use base::linux::max_open_files;
12 use base::RawDescriptor;
13 use cros_async::Executor;
14 use minijail::Minijail;
15
16 use crate::virtio::vhost::user::device::fs::FsBackend;
17 use crate::virtio::vhost::user::device::fs::Options;
18 use crate::virtio::vhost::user::device::listener::sys::VhostUserListener;
19 use crate::virtio::vhost::user::device::listener::VhostUserListenerTrait;
20
default_uidmap() -> String21 fn default_uidmap() -> String {
22 // SAFETY: trivially safe
23 let euid = unsafe { libc::geteuid() };
24 format!("{} {} 1", euid, euid)
25 }
26
default_gidmap() -> String27 fn default_gidmap() -> String {
28 // SAFETY: trivially safe
29 let egid = unsafe { libc::getegid() };
30 format!("{} {} 1", egid, egid)
31 }
32
jail_and_fork( mut keep_rds: Vec<RawDescriptor>, dir_path: PathBuf, uid: u32, gid: u32, uid_map: Option<String>, gid_map: Option<String>, ) -> anyhow::Result<i32>33 fn jail_and_fork(
34 mut keep_rds: Vec<RawDescriptor>,
35 dir_path: PathBuf,
36 uid: u32,
37 gid: u32,
38 uid_map: Option<String>,
39 gid_map: Option<String>,
40 ) -> anyhow::Result<i32> {
41 // Create new minijail sandbox
42 let mut j = Minijail::new()?;
43
44 j.namespace_pids();
45 j.namespace_user();
46 j.namespace_user_disable_setgroups();
47 if uid != 0 {
48 j.change_uid(uid);
49 }
50 if gid != 0 {
51 j.change_gid(gid);
52 }
53 j.uidmap(&uid_map.unwrap_or_else(default_uidmap))?;
54 j.gidmap(&gid_map.unwrap_or_else(default_gidmap))?;
55 j.run_as_init();
56
57 j.namespace_vfs();
58 j.namespace_net();
59 j.no_new_privs();
60
61 // Only pivot_root if we are not re-using the current root directory.
62 if dir_path != Path::new("/") {
63 // It's safe to call `namespace_vfs` multiple times.
64 j.namespace_vfs();
65 j.enter_pivot_root(&dir_path)?;
66 }
67 j.set_remount_mode(libc::MS_SLAVE);
68
69 let limit = max_open_files().context("failed to get max open files")?;
70 j.set_rlimit(libc::RLIMIT_NOFILE as i32, limit, limit)?;
71 // vvu locks around 512k memory. Just give 1M.
72 j.set_rlimit(libc::RLIMIT_MEMLOCK as i32, 1 << 20, 1 << 20)?;
73
74 // Make sure there are no duplicates in keep_rds
75 keep_rds.sort_unstable();
76 keep_rds.dedup();
77
78 // fork on the jail here
79 // SAFETY: trivially safe
80 let pid = unsafe { j.fork(Some(&keep_rds))? };
81
82 if pid > 0 {
83 // Current FS driver jail does not use seccomp and jail_and_fork() does not have other
84 // users, so we do nothing here for seccomp_trace
85 // SAFETY: trivially safe
86 unsafe { libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGTERM) };
87 }
88
89 if pid < 0 {
90 bail!("Fork error! {}", std::io::Error::last_os_error());
91 }
92
93 Ok(pid)
94 }
95
96 /// Starts a vhost-user fs device.
97 /// Returns an error if the given `args` is invalid or the device fails to run.
start_device(opts: Options) -> anyhow::Result<()>98 pub fn start_device(opts: Options) -> anyhow::Result<()> {
99 let ex = Executor::new().context("Failed to create executor")?;
100 let fs_device = FsBackend::new(&ex, &opts.tag, opts.cfg)?;
101
102 let mut keep_rds = fs_device.keep_rds.clone();
103 let listener = VhostUserListener::new_socket(&opts.socket, Some(&mut keep_rds))?;
104
105 base::syslog::push_descriptors(&mut keep_rds);
106 cros_tracing::push_descriptors!(&mut keep_rds);
107 metrics::push_descriptors(&mut keep_rds);
108
109 let pid = jail_and_fork(
110 keep_rds,
111 opts.shared_dir,
112 opts.uid,
113 opts.gid,
114 opts.uid_map,
115 opts.gid_map,
116 )?;
117
118 // Parent, nothing to do but wait and then exit
119 if pid != 0 {
120 // SAFETY: trivially safe
121 unsafe { libc::waitpid(pid, std::ptr::null_mut(), 0) };
122 return Ok(());
123 }
124
125 // We need to set the no setuid fixup secure bit so that we don't drop capabilities when
126 // changing the thread uid/gid. Without this, creating new entries can fail in some corner
127 // cases.
128 const SECBIT_NO_SETUID_FIXUP: i32 = 1 << 2;
129
130 // TODO(crbug.com/1199487): Remove this once libc provides the wrapper for all targets.
131 #[cfg(target_os = "linux")]
132 {
133 // SAFETY:
134 // Safe because this doesn't modify any memory and we check the return value.
135 let mut securebits = unsafe { libc::prctl(libc::PR_GET_SECUREBITS) };
136 if securebits < 0 {
137 bail!(io::Error::last_os_error());
138 }
139 securebits |= SECBIT_NO_SETUID_FIXUP;
140 // SAFETY:
141 // Safe because this doesn't modify any memory and we check the return value.
142 let ret = unsafe { libc::prctl(libc::PR_SET_SECUREBITS, securebits) };
143 if ret < 0 {
144 bail!(io::Error::last_os_error());
145 }
146 }
147
148 // run_until() returns an Result<Result<..>> which the ? operator lets us flatten.
149 ex.run_until(listener.run_backend(fs_device, &ex))?
150 }
151